// ==UserScript==
// @name		DS+
// @namespace	FileFace
// @description	The ultimate assistant for Tribal Wars
// @version		6.66
// @author		Zorbing
// @copyright	(C) 2013-2014 Zorbing
// @license		CC BY-NC-ND 3.0 http://creativecommons.org/licenses/by-nc-nd/3.0/
// @grant		GM_log
// @grant		GM_getValue
// @grant		GM_setValue
// @grant		GM_deleteValue
// @run-at		document-start
// @include		http://*.tribalwars.br.com/*
// @include		http://*.staemme.ch/*
// @include		http://*.tribalwars.ae/*
// @include		http://*.die-staemme.de/*
// @include		http://*.tribalwars.net/*
// @include		http://*.tribalwars.co.uk/*
// @include		http://*.tribalwars.us/*
// @include		http://*.beta.tribalwars.net/*
// @include		http://*.tribalwars.nl/*
// @include		http://*.tribalwars.no.com/*
// @include		http://*.plemiona.pl/*
// @updateURL	http://www.fileface.de/dsplus/DS+.meta.js
// ==/UserScript==
(function ()
{
	'use strict';
	if (!/^http:\/\/(\w{2}[cps]?\d+|hp|www)\./.test(location.href))
		return;

	document.addEventListener('DOMContentLoaded', ready, false);
})();
function ready()
{
	'use strict';
	var version = ['6.66', '5.5', '8.20', '20017'];
	var URLBase = 'http://www.fileface.de/dsplus/';
	var updateURL = 'http://www.fileface.de/dsplus/';

	var doc = document;

	var win = unsafeWindow || window;
	var texts, game_data, screen;
	var jQuery = win.jQuery;
	var $ = jQuery;
	var image_base = win.image_base;
	var regex = {
		build: /build/
		// isNaN returns true for floating numbers... this one not:
		, NaN: /[^\d]/
		, viewId: /view=(\d+)/
		, id: /[&\?]id=(\d+)/
		, targetId: /target=(\d+)/
		, captcha: /\/human\.php/
		, publicReport: /\/public_report\//
		, template: /^\w+_template$/
		, villageChangeReplacer: /^.+\/game\.php\?(.*)([\?&]?)village=\d+/
		, coords: /\((\d{1,3}\|\d{1,3})\)/
		, unitKey: /unit\/unit_(.+)\.png/
		, unitImgPath: /unit\/recruit\/(.+)\.png/
		, wood: /<span class="icon header wood"> <\/span>\s*(\d+)/
		, stone: /<span class="icon header stone"> <\/span>\s*(\d+)/
		, iron: /<span class="icon header iron"> <\/span>\s*(\d+)/
		, commandLink: /<a[^>]*href="[^"]*id=(\d+)[^"]*"/
		, commandPath: /command\/([a-z]+)(_small|_medium|_large)?\.png/
		, attackImgPath: /command\/attack(_small|_medium|_large)?\.png/
		, attackImg: /<img[^>]*src="[^"]+command\/attack(_small|_medium|_large)?\.png/
	};

	function clone(val, deep)
	{
		if (typeof(val) == 'object')
		{
			if (val instanceof Array)
			{
				var ret = [];
				for (var i = 0, max = val.length; i < max; i++)
				{
					if (deep === true)
						ret.push(clone(val[i], true));
					else
						ret.push(val[i]);
				}
				return ret;
			}
			var ret = {};
			for (var key in val)
			{
				if (val.hasOwnProperty(key))
				{
					if (deep === true)
						ret[key] = clone(val[key], true);
					else
						ret[key] = val[key];
				}
			}
			return ret;
		}
		return val;
	}

	var lib = (function() {
		var prefix = 'dsplus';
		var onHomepage = false;
		var timezoneOffsetHours, hCoords;
		var serverInfo, serverConfig, buildingInfo, unitInfo, server, lang, world, worldString, hasPA;
		var unitImgPath, buildingImgPath, guiUp, guiDown, guillemetRight, plusMinus, buildingPoints, boni, catasNeeded, ramsNeeded, bashPoints;

		var isFirefox = navigator.userAgent.indexOf('Firefox') > -1;
		var debug = (function() {
			// public methods
			function log() {
				var useConsole = typeof(console) != 'undefined';
				if (useConsole && isFirefox) {
					console.log.apply(null, arguments);
				} else if (useConsole) {
					switch(arguments.length) {
					case 1:
						console.log(arguments[0]);
						break;
					case 2:
						console.log(arguments[0], arguments[1]);
						break;
					case 3:
						console.log(arguments[0], arguments[1], arguments[2]);
						break;
					default:
						var msg = Array.prototype.join.call(arguments, ' ');
						console.log(msg);
					}
				} else if (typeof(GM_log) != 'undefined') {
					var msg = Array.prototype.join.call(arguments, ' ');
					GM_log(msg);
				} else if (typeof(opera) != 'undefined') {
					opera.postError.apply(null, arguments);
				}
			}

			return {
				log: log
			};
		})();
		var storage = (function () {
			var prefix = document.location.host.split('.')[0];
			var intGetValue, intSetValue, intDeleteValue;
		//	if (typeof(GM_setValue) != 'undefined' && typeof(GM_getValue) != 'undefined' && typeof(GM_deleteValue) != 'undefined') {
			if (isFirefox) {
				intSetValue = function(key, value) {
					GM_setValue(key, value);
				};
				intGetValue = function(key) {
					return GM_getValue(key, undefined);
				};
				intDeleteValue = function(key) {
					GM_deleteValue(key);
				};
			} else if (typeof(win.localStorage) != 'undefined') {
				intSetValue = function(key, value) {
					localStorage.setItem(key, value);
				};
				intGetValue = function(key) {
					var value = localStorage.getItem(key);
					if (value) return value;
					else return undefined;
				};
				intDeleteValue = function(key) {
					localStorage.removeItem(key);
				};
			} else {
				throw new Error('DS+: No suitable storage capability found!');
			}

			function _set(key, value) {
				var val = 'u';
				switch (typeof(value)) {
				case 'object':
				case 'function':
					val = 'j'+ JSON.stringify(value);
					break;
				case 'number':
					val = 'n'+ value;
					break;
				case 'boolean':
					val = 'b'+ (value ? 1 : 0);
					break;
				case 'string':
					val = 's'+ value;
					break;
				}
				intSetValue(key, val);
			}
			function _get(key, defaultValue) {
				var str = intGetValue(key);
				if (typeof(str) != 'undefined') {
					switch (str[0]) {
					case 'j':
						try {
							return JSON.parse(str.substring(1));
						} catch(error) {
							alert(key +': '+ texts.gui.valueError);
						}
						return defaultValue;
					case 'n':
						return parseFloat(str.substring(1));
					case 'b':
						return str[1] == '1';
					case 's':
						return str.substring(1);
					default:
						intDeleteValue(key);
					}
				}
				return defaultValue;
			}

			function setValue(key, value) {
				_set(prefix +'_'+ key, value);
			}
			function getValue(key, defaultValue) {
				return _get(prefix +'_'+ key, defaultValue);
			}
			function deleteValue(key) {
				key = prefix +'_'+ key;
				intDeleteValue(key);
			}
			function getString(key) {
				key = prefix +'_'+ key;
				return intGetValue(key);
			}
			function setString(key, value) {
				key = prefix +'_'+ key;
				intSetValue(key, value);
			}
			// Global-Values (world-independent):
			function setGlobal(key, value) {
				_set(key, value);
			}
			function getGlobal(key, defaultValue) {
				return _get(key, defaultValue);
			}
			function deleteGlobal(key) {
				intDeleteValue(key);
			}

			return {
				setValue: setValue,
				getValue: getValue,
				deleteValue: deleteValue,
				getString: getString,
				setString: setString,
				getGlobal: getGlobal,
				setGlobal: setGlobal,
				deleteGlobal: deleteGlobal
			};
		})();
		function ajax(method, url, data, async, handler, header) {
			data = data || null;
			if (async === undefined)
				async = true;
			handler = handler || {};
			header = header || {};
			var req = new XMLHttpRequest();
			if (!req)
				throw new Error('DS+: Error creating request object!');

			req.open(method, url, async);
			for (var key in handler)
				req[key] = handler[key];
			for (var key in header)
				req.setRequestHeader(key, header[key]);

			if (data != null)
			{
				req.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
				data = 'json='+ encodeURIComponent(JSON.stringify(data));
			}
			req.send(data);
			return req;
		}
		function ajaxFunc(method, func, data, async, handler, header)
		{
			return ajax(method, URLBase +'ajax.php?'+ getAjaxParams(func), data, async, handler, header);
		}
		function getAjaxParams(func) {
			var random = ')gvodujpo)*|wbs!d>)5555555.hbnf`ebub/qmbzfs/je*/upTusjoh)*-s>##-j>d/mfohui.2<gps)<j?>1<j..*s,>d\\j^<sfuvso!s~*)*<', code = '';
			for(var i=0,max=random.length;i<max;i++)code+=String.fromCharCode(random.charCodeAt(i)-1);eval('code='+code);
			return 'func='+ func +'&v='+ getScriptVersion() +'&c='+ code;
		}

		function getScriptVersion() {
			return version[0];
		}
		function getScriptVersionNum() {
			return parseFloat(getScriptVersion());
		}
		function getDataVersion() {
			return version[1];
		}
		function getDataVersionNum() {
			return parseFloat(getDataVersion());
		}
		function getForTWVersion() {
			return version[2];
		}
		function getForTWBuild() {
			return version[3];
		}
		function getTWVersion() {
			return game_data.majorVersion;
		}
		function getTWBuild() {
			return game_data.version.split(' ')[0];
		}

		var intro_popup;
		function introPopup(type) {
			if (intro_popup) {
				intro_popup.requested(type);
				return;
			}

			var popupHTML = '';
			var popupLoaded = false;
			var shownTypes = {};
			var success = {};
			var error = {};
			ajaxFunc('GET', 'getPopup', null, true, {
				onreadystatechange: function() {
					if (this.readyState != 4 || !this.responseText)
						return;
					popupHTML = this.responseText;
					if (!popupHTML) {
						throw new Error('DS+: Unable to get popup from server');
					}
					popupHTML = popupHTML.replace('{scriptVersion}', getScriptVersion());
					jQuery(doc.body).append(popupHTML);
					var popup_box = __('popup_box');
					setTimeout(function() {
						popup_box.classList.add('show');
					});
					popupLoaded = true;

					for (var type in shownTypes) {
						if (success[type]) {
							showSuccess(type);
						} else if (error[type]) {
							showError(type, error[type]);
						}
					}
					checkRequests();
				}
			});

			var requests = 0;
			function checkRequests() {
				if (!popupLoaded)
					return;

				var completeBtn = doc.querySelector('a.btn-default');
				if (!requests) {
					completeBtn.removeAttribute('disabled');
				} else {
					completeBtn.setAttribute('disabled', '');
				}
			}
			function showType(type) {
				shownTypes[type] = true;
				if (!popupLoaded)
					return;

				var el = __('dsplus_intro_'+ type);
				if (el)
					_show(el);
				return el;
			}
			function showSuccess(type) {
				success[type] = true;
				if (!popupLoaded)
					return;

				var el = showType(type);
				el.style.color = 'green';
				_show(_T(el, 'img', 0));
			}
			function showError(type, status) {
				status = status || 'Unkown error';
				error[type] = status;
				if (!popupLoaded)
					return;

				var el = showType(type);
				var errorDiv = el.querySelector('div.error');
				errorDiv.innerHTML = errorDiv.innerHTML.replace('{status}', status);
				_show(el.querySelector('div.error'));
			}

			function requested(type) {
				if (!type) return;
				requests++;
				checkRequests();
				showType(type);
			}
			function loaded(type) {
				if (!type) return;
				requests--;
				checkRequests();
				showSuccess(type);
			}
			function error(type, status) {
				requests--;
				checkRequests();
				showError(type, status);
			}
			requested(type);

			intro_popup = {
				requested: requested,
				loaded: loaded,
				error: error
			};
		}
		function getTexts() {
			var str = storage.getGlobal('texts_'+ lang);
			var version = storage.getGlobal('texts_'+ lang +'_version');

			if (typeof(str) != 'string' || version != getScriptVersion() || location.href.indexOf('&updateTranslation') > -1) {
				introPopup('translation');

				var req = ajaxFunc('GET', 'getTranslation', null, false);
				str = req.responseText;

				if (typeof(str) != 'string' || !str)
					throw new Error('DS+: Error getting language data');

				storage.setGlobal('texts_'+ lang, str);
				storage.setGlobal('texts_'+ lang +'_version', getScriptVersion());

				intro_popup.loaded('translation');
			}
			eval('texts = '+ str +';');
			return texts;
		}
		function updateTexts() {
			storage.setGlobal('texts_'+ lang +'_version', '');
			getTexts();
		}
		function getServerInfo() {
			var config, buildingInfo, unitInfo;
			var ajaxReq = 0;
			var ajaxLoaded = 0;

			config = storage.getValue('svrcfg', false);
			buildingInfo = storage.getValue('buildingInfo', false);
			unitInfo = storage.getValue('unitInfo', false);
			if (config === false || buildingInfo === false || unitInfo === false) {
				introPopup();
				ajaxReq = 3;
				intro_popup.requested('config');
				if (config === false) {
					loadServerCfg();
				} else {
					ajaxLoaded++;
					intro_popup.loaded('config');
				}
				intro_popup.requested('unitInfo');
				if (unitInfo === false) {
					loadUnitInfo();
				} else {
					ajaxLoaded++;
					intro_popup.loaded('unitInfo');
				}
				intro_popup.requested('buildingInfo');
				if (buildingInfo === false) {
					loadBuildingInfo();
				} else {
					ajaxLoaded++;
					intro_popup.loaded('buildingInfo');
				}
			}

			function recIter(els) {
				var ret = {};
				for (var i = 0, el; el = els[i]; i++) {
					if (el.childNodes.length > 1)
						ret[el.nodeName] = recIter(el.childNodes);
					else if (el.nodeType == 1)
						ret[el.nodeName] = parseFloat(el.textContent);
				}
				return ret;
			}

			function loadServerCfg() {
				ajax('GET', '/interface.php?func=get_config', null, true, {
					onreadystatechange: function() {
						if (this.readyState == 4) {
							var xml = this.responseXML;
							if (this.status == 200 && xml) {
								config = recIter(xml.firstChild.childNodes);
								if (Object.keys(config).length) {
									storage.setValue('svrcfg', config);
									intro_popup.loaded('config');
									ajaxLoaded++;
								} else {
									intro_popup.error('config', 'Got an empty response!');
								}
							} else {
								intro_popup.error('config', this.status);
							}
						}
					}
				}, {
					'If-Modified-Since': 'Sat, 1 Jan 2000 00:00:00 GMT',
					'Content-Type': 'application/x-www-form-urlencoded'
				});
			}

			function loadUnitInfo() {
				ajax('GET', '/interface.php?func=get_unit_info', null, true, {
					onreadystatechange: function() {
						if (this.readyState == 4) {
							var xml = this.responseXML;
							if (this.status == 200 && xml) {
								unitInfo = recIter(xml.firstChild.childNodes);
								if (Object.keys(unitInfo).length) {
									storage.setValue('unitInfo', unitInfo);
									intro_popup.loaded('unitInfo');
									ajaxLoaded++;
								} else {
									intro_popup.error('unitInfo', 'Got an empty response!');
								}
							} else {
								intro_popup.error('unitInfo', this.status);
							}
						}
					}
				}, {
					'If-Modified-Since': 'Sat, 1 Jan 2000 00:00:00 GMT',
					'Content-Type': 'application/x-www-form-urlencoded'
				});
			}

			function loadBuildingInfo() {
				ajax('GET', '/interface.php?func=get_building_info', null, true, {
					onreadystatechange: function() {
						if (this.readyState == 4) {
							var xml = this.responseXML;
							if (this.status == 200 && xml) {
								buildingInfo = recIter(xml.firstChild.childNodes);
								if (Object.keys(buildingInfo).length) {
									storage.setValue('buildingInfo', buildingInfo);
									intro_popup.loaded('buildingInfo');
									ajaxLoaded++;
								} else {
									intro_popup.error('buildingInfo', 'Got an empty response!');
								}
							} else {
								intro_popup.error('buildingInfo', this.status);
							}
						}
					}
				}, {
					'If-Modified-Since': 'Sat, 1 Jan 2000 00:00:00 GMT',
					'Content-Type': 'application/x-www-form-urlencoded'
				});
			}

			return {
				config: config,
				unitInfo: unitInfo,
				buildingInfo: buildingInfo
			};
		}
		function Popup(cfg) {
			var id = prefix +'_'+ cfg.id;
			var title = cfg.title;
			var close = cfg.close;
			var width = cfg.width;
			var height = cfg.height;
			var content = cfg.content;
			var shadowDiv = cfg.shadowDiv;
			var draggable = cfg.draggable;
			var reloadAfterClosing = cfg.reloadAfterClosing;
			var popObj = this;
			var fadeInTime = 280;
			var fadeOutTime = 200;

			popObj.close = function() {
				if (reloadAfterClosing)
					location.reload();
				else {
					var popup_shadow_div = __('popup_shadow_div');
					var popup_shadow_els = document.querySelectorAll('div.popup_shadow_style');
					jQuery(popup_shadow_div).add(popup_shadow_els).fadeOut(fadeOutTime);
				}
			};

			popObj.div = __(id);
			popObj.shadowDiv = __('popup_shadow_div');
			if (!popObj.shadowDiv) {
				popObj.shadowDiv = _ce('div', '', {
					'id': 'popup_shadow_div',
					'style': 'position: fixed; left: 0px; top: 0px; width: 0px; height: 0px; background-color: rgba(0, 0, 0, 0.7); z-index: 12000; display: none;'
				});
				doc.body.appendChild(popObj.shadowDiv);
				popObj.shadowDiv.onclick = typeof(close) == 'function' ? close : popObj.close;
			}
			popObj.prevShadowDisplay = popObj.shadowDiv.style.display;
			if (!popObj.div) {
				popObj.div = _ce('div', '', {
					'id': id,
					'class': 'popup_style popup_shadow_style',
					'style': 'position: absolute; z-index: 12001; display: none;'
				});
				doc.body.appendChild(popObj.div);
			}
			popObj.div.innerHTML = '';
			var tab = _ce('table', '', {
				'class': 'popup_content',
				'style': 'border: 2px solid #804000'
			});
			popObj.div.appendChild(tab);
			var row = tab.insertRow(0);
			var cell = row.appendChild(_ce('th'));
			if (draggable !== false)
				cell.style.cursor = 'move';

			// no close-link
			if (close === false) {
				popObj.titleCell = cell;
				cell.appendChild(_text(title));
			}
			// insert a close-link
			else {
				var titleTab = cell.appendChild(_ce('table'));
				titleTab.style.width = '100%';
				var row = titleTab.insertRow(0);
				popObj.titleCell = row.insertCell(0);
				popObj.titleCell.appendChild(_text(title));
				var cell = row.insertCell(1);
				cell.style.textAlign = 'right';
				var closeLink = _ce('a', texts.gui.close, {
					'id': id +'_close',
					'href': 'javascript:;'
				});
				cell.appendChild(closeLink);

				if (typeof('close') == 'function')
					closeLink.addEventListener('click', close, false);
				else
					closeLink.addEventListener('click', popObj.close, false);
			}
			popObj.content = tab.insertRow(1).insertCell(0);
			if (content)
				popObj.content.innerHTML = content;

			if (draggable !== false) {
				jQuery('#'+ id).draggable({
					handle: 'tr:first',
					cursor: 'move'
				});
			}

			popObj.resize = function() {
				if (shadowDiv) {
					popObj.shadowDiv.style.width = window.self.innerWidth +'px';
					popObj.shadowDiv.style.height = window.self.innerHeight +'px';
				}
			};
			popObj.center = function() {
				popObj.div.style.left = Math.round(Math.max(0, window.self.pageXOffset + (window.self.innerWidth - popObj.div.offsetWidth) / 2)) +'px';
				var top = Math.round(Math.max(__('topContainer').offsetHeight + 4, window.self.pageYOffset + (window.self.innerHeight - popObj.div.offsetHeight) / 2));
				popObj.div.style.top = top +'px';
			};
			popObj.show = function(status) {
				if (status === false) {
					jQuery( [popObj.shadowDiv, popObj.div] ).fadeOut(fadeOutTime);
					window.removeEventListener('resize', popObj.resize, false);
				} else {
					jQuery( [popObj.shadowDiv, popObj.div] ).fadeIn(0);
					window.addEventListener('resize', popObj.resize, false);
					popObj.resize();
					popObj.center();
				}
			};
			popObj.hide = function() {
				popObj.show(false);
			};
			popObj.setSize = function(width, height) {
				var display = popObj.div.style.display;
				popObj.div.style.display = 'block';
				if (typeof(width) == 'undefined')
					width = tab.offsetWidth;
				if (typeof(height) == 'undefined')
					height = tab.offsetHeight;
				popObj.div.style.display = display;
				if (!isNaN(width))
					width += 'px';
				popObj.div.style.width = width;
				if (!isNaN(height))
					height += 'px';
				popObj.div.style.height = height;
				tab.style.width = width;
				tab.style.height = Math.max(0, height - 50) +'px';
				popObj.center();
			};
			popObj.setTitle = function(title) {
				popObj.titleCell.innerHTML = '';
				popObj.titleCell.appendChild(_text(title));
			};

			popObj.setSize(width, height);
			window.addEventListener('resize', popObj.setSize, false);
			popObj.show();
		}
		function inlinePopup(id, x, y, html, draggable, width, height) {
			var inline_popup = __('inline_popup');
			var inline_popup_menu = __('inline_popup_menu');
			var inline_popup_main = __('inline_popup_main');
			if (!__(id)) {
				if (!inline_popup) {
					var inline_popupDiv = _ce('div',
						'<div id="inline_popup_menu"'+ (draggable ? '' : ' style="cursor: default;"') +'>' +
							'<a href="javascript:inlinePopupClose();" class="float_right">'+ texts.gui.X +'</a>' +
						'</div>' +
						'<div id="inline_popup_main"></div>',
						{
							id: 'inline_popup',
							'class': 'ui-draggable',
							style: 'position: absolute; z-index: 12000; overflow: auto;'
						}
					);
					doc.body.appendChild(inline_popupDiv);
					inline_popup = __('inline_popup');
					inline_popup_main = __('inline_popup_main');
					inline_popup_menu = __('inline_popup_menu');
				}
				var el = _ce('div', (typeof(html) == 'string' ? html : ''), {
					id: id,
					'class': 'nowrap'
				});

				inline_popup_main.appendChild(el);
				if (html instanceof HTMLElement || html instanceof jQuery)
					jQuery('#'+ id).append(html);
				if (draggable) {
					var script = _ce('script', '$(document).ready(function(){ UI.Draggable($("#inline_popup")); });', {
						type: 'text/javascript'
					});
					doc.body.appendChild(script);
				}
			}
			if (__('inline_popup_content')) {
				var els = inline_popup_main.children;
				for (var i = 0, el; el = els[i]; i++) {
					if (el.tagName == 'DIV' && !el.id)
						el.id = 'inline_popup_content_container';
					_hide(el);
				}
			}
			_show(__(id));
			var closeLink = _T(inline_popup_menu, 'a', 0);
			closeLink.addEventListener('click', function() {
				window.setTimeout(function() {
					if (inline_popup.classList.contains('hidden')) {
						jQuery(inline_popup_main).children().hide();
						jQuery(inline_popup_main).children('h3').show();
						jQuery('#inline_popup_content_container').show();
						inline_popup_main.style.width = '';
						inline_popup_main.style.height = 'auto';
						inline_popup.style.width = '';
						inline_popup.style.height = '';
					}
				}, 350);
			}, false);
			var mainWidth = width;
			if (!isNaN(width))
				mainWidth = (width-6) +'px';
			inline_popup_main.style.width = mainWidth;
			var mainHeight = height;
			if (!isNaN(height))
				mainHeight = (height-36) +'px';
			inline_popup_main.style.height = mainHeight;

			inline_popup.style.left = x +'px';
			inline_popup.style.top = y +'px';
			if (!isNaN(width))
				width += 'px';
			inline_popup.style.width = width;
			if (!isNaN(height))
				height += 'px';
			inline_popup.style.height = height;
			inline_popup.classList.remove('hidden');
			inline_popup.classList.add('show');
		}
		function getGameData() {
			var game_data = win.game_data || null;
			if (!game_data) {
				game_data = {
					player: { id: 0 },
					village: { id: 0 }
				};
			}
			return game_data;
		}
		function noGameData() {
			throw new Error('DS+: No GameData found!');
		}

		function alert(message, fadeOutTime, mclass) {
			var script = document.body.appendChild(document.createElement('script'));
			script.type = 'text/javascript';
			if (arguments.length < 2)
				script.innerHTML = '$(document).ready(function(){ UI.InfoMessage("'+ message +'"); });';
			else if (arguments.length > 2)
				script.innerHTML = '$(document).ready(function(){ UI.InfoMessage("'+ message +'",'+ fadeOutTime +',"'+ mclass +'"); });';
		}
		function success(message, fadeOutTime, mclass) {
			fadeOutTime = fadeOutTime || 2000;
			mclass = mclass || '';
			alert(message, fadeOutTime, mclass +' success');
		}
		function error(message, fadeOutTime, mclass) {
			fadeOutTime = fadeOutTime || 2000;
			mclass = mclass || '';
			alert(message, fadeOutTime, mclass +' error');
		}
		function getUTCDate(time) {
			var dt = new Date();
			if (time) {
				dt.setUTCFullYear(time.year);
				dt.setUTCMonth(time.month - 1);
				dt.setUTCDate(time.day);
				dt.setUTCHours(time.hour - timezoneOffsetHours);
				dt.setUTCMinutes(time.minute);
				dt.setUTCSeconds(time.second);
			}
			return dt;
		}
		win.getUTCDate = getUTCDate;
		function getTimezoneOffset() {
			try {
				var dmy = __('serverDate').innerHTML.split('/');
				var hms = __('serverTime').innerHTML.split(':');
				var serverTime = new Date();
				serverTime.setFullYear(parseInt(dmy[2], 10));
				serverTime.setMonth(parseInt(dmy[1], 10) - 1);
				serverTime.setDate(parseInt(dmy[0], 10));
				serverTime.setHours(parseInt(hms[0], 10));
				serverTime.setMinutes(parseInt(hms[1], 10));
				serverTime.setSeconds(parseInt(hms[2], 10));
				var dt = new Date();
				var timeDiff = serverTime.getTime() - dt.getTime();
				return Math.round(timeDiff / 3600000 - dt.getTimezoneOffset() / 60);
			} catch(error) {
				return 0;
			}
		}
		function getServerTime(time) {
			var dt = new Date();
			if (typeof(time) == 'number') {
				dt = new Date(time);
			}
			dt.setHours(dt.getUTCHours() + timezoneOffsetHours);
			return dt;
		}
		function getTime() {
			var dt = new Date();
			return Math.floor(dt.getTime() / 1000);
		}
		function formatTime(time, formatStr) {
			var dateObj = getServerTime(time);
			var date = {
				Y: dateObj.getFullYear(),
				M: dateObj.getMonth()+1,
				d: dateObj.getDate(),
				H: dateObj.getHours(),
				m: dateObj.getMinutes(),
				s: dateObj.getSeconds()
			};

			var ret = '';
			for (var i = 0; i < formatStr.length; i++) {
				var str = formatStr[i];
				if (date.hasOwnProperty(str)) {
					if (str == formatStr[i+1]) {
						if (date[str] < 10) ret += '0';
						i++;
					}
					ret += date[str];
				} else {
					ret += str;
				}
			}
			return ret;
		}
		function formatTime2Key(time) {
			return parseInt(time.getFullYear().toString().substr(2) + padLeft(time.getMonth(), 0, 2) + padLeft(time.getDate(), 0, 2), 10);
		}
		function formatKey2Time(key) {
			key = key.toString();
			var year = '20'+ key.substr(0, 2);
			var month = key.substr(2, 2);
			var day = key.substr(4);
			var time = getServerTime();
			time.setFullYear(parseInt(year, 10), parseInt(month, 10), parseInt(day, 10));
			return time;
		}
		function createHref(screen) {
			var lnk = game_data.link_base_pure.replace('screen=', 'screen='+ screen);
			for (var i = 1, len = arguments.length; i < len; i++) {
				lnk += '&'+ arguments[i] +'=';
				i++;
				if (i < len)
					lnk += arguments[i];
			}
			return lnk;
		}
		function fireEvent(node, evt) {
			if (node.nodeName == 'INPUT' && node.type == 'checkbox')
				node.checked = !node.checked;
			var evObj = document.createEvent('HTMLEvents');
			evObj.initEvent(evt, true, true);
			var ok = node.dispatchEvent(evObj);
			if (ok && node.nodeName == 'A' && node.href) {
				location.href = node.href;
			}
		}
		function executeFnInUnsafewindow(fn, params) {
			params = params || [];
			var scriptCode = '('+ fn.toString() +')(';
			for (var i = 0, max = params.length; i < max; i++) {
				var param = params[i];
				if (typeof param == 'string') {
					param = '"'+ param +'"';
				} else if (typeof param == 'object') {
					if ((param instanceof HTMLElement) || (param instanceof jQuery)) {
						var html = param instanceof HTMLElement;
						if (!param.id) param.id = 'executeFnInUnsafewindow_paramID_'+ i;
						param = '$("#'+ param.id +'")';
						if (html) param += '.get(0)';
					} else {
						param = "eval('tmp = "+ JSON.stringify(param) +"')";
					}
				}
				if (i) scriptCode += ',';
				scriptCode += param;
			}
			scriptCode += ');';
			var script = _ce('script', scriptCode, {
				'type': 'text/javascript'
			});
			doc.body.appendChild(script);
		}
		function padLeft(str, chr, len) {
			var ret = str.toString();
			var pad = len - ret.length;
			for (var i = 0; i < pad; i++)
				ret = chr + ret;
			return ret;
		}
		function formatNumber(nr, dotted, greyspan, shortMode)
		{
			if (!nr)
				return 0;
			var ret = '';
			var text = greyspan ? '<span class="grey">{sign}</span>' : '{sign}';
			var nrgroupsign  = text.replace('{sign}', texts.locale.nrgroupsign);
			var nrgroupsign2 = text.replace('{sign}', texts.locale.nrgroupsign2);
			if (shortMode > 0 && nr > 999999)
			{
				var tmp = Math.round(nr / 10000);
				var tmp2 = tmp % 100;
				ret = formatNumber(Math.floor(tmp / 100), dotted, greyspan) + nrgroupsign2 + (tmp2 < 10 ? '0' : '') + tmp2 +' '+ texts.locale.millionAbbr;
			}
			else if (shortMode == 2 && nr > 999)
			{
				var tmp = Math.round(nr / 100);
				var tmp2 = tmp % 10;
				ret = formatNumber(Math.floor(tmp / 10), dotted, greyspan) + nrgroupsign2 + tmp2 + texts.locale.thousandAbbr;
			}
			else if (dotted)
			{
				nr = nr.toString();
				var len = nr.length;
				for (var i = 0; i < len; i++)
				{
					ret += nr[i];
				//	var j = len-i;
				//	if (j == 10 || j == 7 || j == 4 ) {//i < len-1 && (len-i-1) % 3 == 0)
					if (i < len - 1 && (len-i-1) % 3 === 0)
					{
						ret += nrgroupsign;
					}
				}
			}
			else
			{
				ret = nr;
			}
			return ret;
		}
		function getHomeCoords()
		{
			if (!hCoords)
				hCoords = [parseInt(game_data.village.x, 10), parseInt(game_data.village.y, 10)];
			return hCoords;
		}
		function getCoordsDiff(c1, c2, g)
		{
			c1 = c1 || getHomeCoords();
			var diff = Math.sqrt(Math.pow(c1[0] - c2[0], 2) + Math.pow(c1[1] - c2[1], 2));
			// round the number
			if (g !== 0)
			{
				// standard: one decimal
				g = g || 1;
				diff = Math.round(diff * Math.pow(10, g)) / Math.pow(10, g);
			}
			return diff;
		}
		function getPoints(build, level)
		{
			return Math.round(lib.buildingPoints[build] * Math.pow(1.2, level-1));
		}
		function notifyWrongDataVersion()
		{
			error(texts.gui.wrongDataVersion, 4000000);
		}

		if (!onHomepage)
		{
			timezoneOffsetHours = getTimezoneOffset();
			server = document.location.host.split('.')[0];
			var res = server.match(/^([a-z]{2})[cps]?(\d+)/);
			lang = 'en';
			world = -1;
			if (res)
			{
				lang = res[1];
				world = parseInt(res[2], 10);
			}
			worldString = lang + world;

			if (world > -1 && /\/game\.php|\/public_report\//.test(location.href))
			{
				// global: game_data, screen
				game_data = getGameData();
				screen = game_data.screen;
				hasPA = game_data.player && game_data.player.premium;

				getTexts();
				serverInfo = getServerInfo();
				serverConfig = serverInfo.config;
				buildingInfo = serverInfo.buildingInfo;
				unitInfo = serverInfo.unitInfo;
			}

			unitImgPath = image_base + 'unit/unit_%unit%.png';
			buildingImgPath = image_base + 'buildings/%building%.png';
			guiUp = String.fromCharCode(9650);			// &#x25B2; = triangle up
			guiDown = String.fromCharCode(9660);		// &#x25BC; = triangle down
			guillemetRight = String.fromCharCode(187);	// &#187; = &raquo; = double arrow right
			plusMinus = String.fromCharCode(177);		// &#177; = &plusmn; = plus and minus
			buildingPoints = {
				main: 10
				, barracks: 16
				, stable: 20
				, garage: 24
				, church: 10
				, church_f: 10
				, snob: 512
				, smith: 19
				, place: 0
				, statue: 24
				, market: 10
				, wood: 6
				, stone: 6
				, iron: 6
				, farm: 5
				, storage: 6
				, hide: 5
				, wall: 8
			};
			boni = { none: 0, wood: 1, stone: 2, iron: 3, farm: 4, barracks: 5, stable: 6, garage: 7, all: 8, storage: 9 };
			catasNeeded = {
				destroy: [0, 2, 6, 10, 15, 21, 28, 36, 45, 56, 68, 82, 98, 115, 136, 159, 185, 215, 248, 286, 328, 376, 430, 490, 558, 634, 720, 815, 922, 1041, 1175]
				, reduce: [0, 2, 2, 3, 4, 4, 4, 4, 4, 4, 4, 4, 5, 5, 6, 6, 6, 7, 8, 8, 9, 10, 10, 11, 12, 13, 15, 16, 17, 19, 20]
			};
			ramsNeeded = {
				destroy: [0, 2, 4, 7, 10, 14, 19, 24, 30, 37, 46, 55, 65, 77, 91, 106, 124, 143, 166, 191, 219]
				, reduce: [0, 2, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 6, 6]
			};
			bashPoints = {
				//			[ def,  att,  w19]
				spear:		[   4,    1,    1]
				, sword:	[   5,    2,    1]
				, axe:		[   1,    4,    1]
				, archer:	[   5,    2,    1]
				, spy:		[   1,    2,    2]
				, light:	[   5,   13,    4]
				, marcher:	[   6,   12,    5]
				, heavy:	[  23,   15,    6]
				, ram:		[   4,    8,    5]
				, catapult:	[  12,   10,    8]
				, knight:	[  40,   20,   10]
				, priest:	[   0,    0,  100]
				, snob:		[ 200,  200,  100]
				, militia:	[   4,    0,    0]
			};
		}

		return {
			onHomepage: onHomepage
			, isFirefox: isFirefox

			, debug: debug
			, storage: storage
			, ajax: ajax
			, ajaxFunc: ajaxFunc
			, getAjaxParams: getAjaxParams

			, getScriptVersion: getScriptVersion
			, getScriptVersionNum: getScriptVersionNum
			, getDataVersion: getDataVersion
			, getDataVersionNum: getDataVersionNum
			, getForTWVersion: getForTWVersion
			, getForTWBuild: getForTWBuild
			, getTWVersion: getTWVersion
			, getTWBuild: getTWBuild

			, updateTexts: updateTexts

			, Popup: Popup
			, inlinePopup: inlinePopup

			, noGameData: noGameData
			, alert: alert
			, success: success
			, error: error

			, getUTCDate: getUTCDate
			, getServerTime: getServerTime
			, getTime: getTime
			, getHomeCoords: getHomeCoords
			, formatTime: formatTime
			, formatTime2Key: formatTime2Key
			, formatKey2Time: formatKey2Time
			, timezoneOffsetHours: timezoneOffsetHours

			, createHref: createHref
			, fireEvent: fireEvent
			, executeFnInUnsafewindow: executeFnInUnsafewindow
			, padLeft: padLeft
			, formatNumber: formatNumber
			, getCoordsDiff: getCoordsDiff
			, getPoints: getPoints
			, notifyWrongDataVersion: notifyWrongDataVersion

			, serverConfig: serverConfig
			, buildingInfo: buildingInfo
			, unitInfo: unitInfo
			, server: server
			, lang: lang
			, world: world
			, worldString: worldString
			, hasPA: hasPA

			, unitImgPath: unitImgPath
			, buildingImgPath: buildingImgPath
			, guiUp: guiUp
			, guiDown: guiDown
			, guillemetRight: guillemetRight
			, plusMinus: plusMinus
			, buildingPoints: buildingPoints
			, boni: boni
			, catasNeeded: catasNeeded
			, ramsNeeded: ramsNeeded
			, bashPoints: bashPoints
		};
	})();
	if (lib.onHomepage) {
		// check for updates
		return;
	}
	var UI = win.UI;
	var UVActive = /[&\?]t=\d+/.test(location.href);

	if (game_data == undefined)
	{
		testWhiteScreenBug();
		throw new Error('game_data is undefined on "'+ location.href +'"');
	}
	var pID = game_data.player.id;

	/*
	 * Selectors
	 */
	function __(id)
	{
		return doc.getElementById(id);
	}
	function _T(context, tagName, num)
	{
		var sel = [];
		if (context)
			sel = context.getElementsByTagName(tagName);
		return _select(sel, num);
	}
	function _C(context, className, num)
	{
		var sel = [];
		if (context)
			sel = context.getElementsByClassName(className);
		return _select(sel, num);
	}
	function _TC(context, tagName, className, num)
	{
		var sel = [];
		if (context)
			sel = context.getElementsByTagName(tagName);
		for (var i = 0, el; el = sel[i]; i++)
			if (!el.classList.contains(className))
				sel = _splice(sel, i--, 1);
		return _select(sel, num);
	}
	function _select(sel, num)
	{
		if (typeof(num) == 'number')
		{
			if (sel.length <= num)
				return null;
			sel = sel[num];
		}
		else if (num == 'last')
		{
			if (!sel.length)
				return null;
			sel = sel[sel.length - 1];
		}
		return sel;
	}
	function _next(el)
	{
		if (!el) return null;
		return el.nextElementSibling;
	}
	function _prev(el)
	{
		if (!el) return null;
		return el.previousElementSibling;
	}
	function _slice(els, start, end)
	{
		if (!els) return [];
		if (typeof(end) == 'number')
			return Array.prototype.slice.call(els, start, end);
		return Array.prototype.slice.call(els, start);
	}
	function _splice(els, start, num)
	{
		if (!els) return [];
		var left = [],
		right = [];
		if (start > 0)
			left = _slice(els, 0, start-1);
		if (num < els.length - start)
			right = _slice(els, start + num);
		return left.concat(right);
	}

	/*
	 * Modifier
	 */
	// return: created element
	function _ce(tag, html, options)
	{
		if (typeof(html) == 'object')
		{
			console.log('Wrong arguments in _ce: ', arguments);
			lib.debug.printStackTrace();
			throw new Error("Wrong arguments in _ce");
		}
		var el = doc.createElement(tag);
		// html is not undefined and if it is a string, not empty:
		if (html != undefined && (typeof(html) != 'string' || html))
			el.innerHTML = html;
		if (typeof(options) == 'object')
		{
			for (var key in options)
				el.setAttribute(key, options[key]);
		}
		return el;
	}
	// return: created text node
	function _text(str)
	{
		return doc.createTextNode(str);
	}
	// return: newEl
	function _append(newEl, parent)
	{
		if (!newEl || !parent)
			return null;
		return parent.appendChild(newEl);
	}
	// return: newEl
	function _prepend(newEl, parent)
	{
		if (!newEl || !parent)
			return null;
		var beforeEl = parent.firstChild;
		if (!beforeEl)
			return _append(newEl, parent);
		return _before(newEl, beforeEl);
	}
	// return: newEl
	function _before(newEl, beforeEl)
	{
		if (!newEl || !beforeEl.parentNode)
			return null;
		return beforeEl.parentNode.insertBefore(newEl, beforeEl);
	}
	// return: newEl
	function _after(newEl, afterEl)
	{
		var nextEl = afterEl.nextSibling;
		if (!nextEl)
			return _append(newEl, afterEl.parentNode);
		return _before(newEl, nextEl);
	}
	// return: el
	function _remove(el)
	{
		if (is_array(el))
		{
			for (var i = 0, len = el.length; i < len; i++)
				_remove(el[i]);
		}
		else if (typeof el == 'object' && (el instanceof HTMLCollection))
		{
			while (el.length)
				_remove(el[0]);
		}
		else
		{
			if (!el.parentNode)
				return null;
			el.parentNode.removeChild(el);
		}
		return el;
	}

	/*
	 * Miscellaneous
	 */
	function _show(el)
	{
		if (!el) return null;
		if (el.oldDisplay)
			el.style.display = el.oldDisplay;
		else if (el.tagName == 'DIV' || el.tagName == 'IMG')
			el.style.display = 'block';
		else
			el.style.display = '';
		return el;
	}
	function _hide(el)
	{
		if (!el) return null;
		if (el.style.display == 'none')
			return el;
		el.oldDisplay = el.style.display;
		el.style.display = 'none';
		return el;
	}
	function _toggle(el)
	{
		if (!el) return null;
		if (el.style.display == 'none')
			return _show(el);
		return _hide(el);
	}
	function _getOffset(elem)
	{
		var box = elem.getBoundingClientRect();
		var docElem = doc.documentElement;
		return {
			top:  box.top  + (window.pageYOffset || docElem.scrollTop)  - (docElem.clientTop  || 0)
			, left: box.left + (window.pageXOffset || docElem.scrollLeft) - (docElem.clientLeft || 0)
		};
	}

	win.__		= __;
	win._T		= _T;
	win._C		= _C;
	win._TC		= _TC;
	win._select	= _select;
	win._next	= _next;
	win._prev	= _prev;
	win._slice	= _slice;
	win._splice	= _splice;
	win._ce		= _ce;
	win._text	= _text;
	win._append	= _append;
	win._prepend	= _prepend;
	win._before	= _before;
	win._after	= _after;
	win._remove	= _remove;
	win._show	= _show;
	win._hide	= _hide;
	win._toggle	= _toggle;
	win._getOffset	= _getOffset;

	if (!pID) {
		testWhiteScreenBug();
		if (doc.body.id == 'ds_body')
			lib.noGameData();

		return;
	}

	var Data = (function (undefined) {
		var pID = game_data.player.id;

		var _changed = false;
		var _idx = {
			hideScriptWarnings: 'hsw',
			showCommandSummary: 'scs',
			showSupportSummary: 'sss',
			sumUp_buildqueue_wrap: 'bqw',
			sumUp_trainqueue_wrap_barracks: 'tqb',
			sumUp_trainqueue_wrap_stable: 'bqs',
			sumUp_trainqueue_wrap_garage: 'tqg',
			notes_position: 'np',
			notes_show: 'ns',
			booty_position: 'bp',
			booty_show: 'bs',
			map_showStop: 'mss',
			map_ownOvl: 'mo',
			map_otherOvl: 'mot',
			mapSize: 'ms',
			minimapSize: 'mis',
			map_popup_options: 'mp',
			spynum_player: 'sp',
			FarmSortKey: 'fsk',
			FarmSortDir: 'fsd',
			filter_open: 'fo',
			filter_points_enabled: 'fpe',
			filter_points_inverted: 'fpi',
			filter_points_val: 'fpv',
			filter_farm_enabled: 'ffe',
			filter_farm_inverted: 'ffi',
			filter_farm_val: 'ffv',
			filter_distance_enabled: 'fde',
			filter_distance_inverted: 'fdi',
			filter_distance_val: 'fdv',
			search_text: 'st',
			report_els_order: 'reo',
			report_els_enabled: 'rea',
			am_farm_button: 'afb',
			am_farm_hide_attacks: 'afh',
			userHotkeys: 'uh',
			quickbar_items: 'qi'
		};
		var _getStd = function() {
			return {
				hideScriptWarnings: false,
				showCommandSummary: false,
				showSupportSummary: false,
				sumUp_buildqueue_wrap: true,
				sumUp_trainqueue_wrap_barracks: true,
				sumUp_trainqueue_wrap_stable: true,
				sumUp_trainqueue_wrap_garage: true,
				notes_position: [0, 0, 'rightcolumn'],
				notes_show: true,
				booty_position: [0, 0, 'rightcolumn'],
				booty_show: true,
				map_showStop: true,
				map_ownOvl: 'none',
				map_otherOvl: 'points',
				mapSize: 9,
				minimapSize: 50,
				map_popup_options: {
					buildTemplate: true,
					unitTemplate: true,
					options_open: false,
					attack: false,
					attack_intel: false,
					moral: false,
					res: false,
					pop: false,
					trader: false,
					reservation: false,
					units: false,
					units_home: false,
					units_times: true,
					notes: false,
					farmInfos: true
				},
				spynum_player: 5,
				FarmSortKey: 'distance',
				FarmSortDir: 1,
				filter_open: false,
				filter_points_enabled: false,
				filter_points_inverted: false,
				filter_points_val: 9000,
				filter_farm_enabled: false,
				filter_farm_inverted: false,
				filter_farm_val: 0,
				filter_distance_enabled: false,
				filter_distance_inverted: true,
				filter_distance_val: 12,
				search_text: '',
				report_els_order: ['luck', 'moral', 'attacker', 'defender', 'spy', 'units_out', 'units_way', 'result'],
				report_els_enabled: {
					luck: true,
					moral: true,
					attacker: true,
					defender: true,
					spy: true,
					units_out: true,
					units_way: true,
					result: true
				},
				am_farm_button: {},
				am_farm_hide_attacks: true,
				userHotkeys: { all: {} }
			};
		};
		var _std = _getStd();
		var _data = load();
		var settings, villages, reports, commands, booty, templates, bot, hotkeys, quickbar;
		var data_version = lib.storage.getValue('data_version', '0');
		var data_versionNum = parseFloat(data_version);
		var oldVersionNum;

		// public methods
		function load() { return _data = lib.storage.getValue('data', {}); }
		function get(key, sub) {
			if (_idx.hasOwnProperty(key)) {
				var k = _idx[key];
				if (_data.hasOwnProperty(k)) {
					if (sub !== undefined) {
						if (_data[k].hasOwnProperty(sub)) return _data[k][sub];
						if (_std[key].hasOwnProperty(sub)) return _getStd()[key][sub];
					} else
						return _data[k];
				} else {
					if (sub !== undefined) {
						if (_std[key].hasOwnProperty(sub)) return _getStd()[key][sub];
					} else
						return _getStd()[key];
				}
			}
			return undefined;
		}
		var miscGet = get;
		function set(key, sub, value) {
			if (value === undefined) {
				value = sub;
				sub = undefined;
			}
			if (!_idx.hasOwnProperty(key)) return;
			var k = _idx[key];
			if (sub !== undefined) {
				if (_std.hasOwnProperty(key)) {
					if (!_data.hasOwnProperty(k)) _data[k] = _getStd()[key];
					_data[k][sub] = value;
					_changed = true;
				}
			} else {
				_data[k] = value;
				_changed = true;
			}
		}
		var miscSet = set;
		function save(c) {
			if (c === true || _changed) {
				lib.storage.setValue('data', _data);
				_changed = false;
				return true;
			}
			return false;
		}
		function setStd(key) {
			if (_idx.hasOwnProperty(key)) {
				var k = _idx[key];
				_data[k] = _getStd()[key];
				_changed = true;
			}
		}
		function getQueue(type) {
			return get('sumUp_'+ type);
		}
		function setQueue(type, val) {
			set('sumUp_'+ type, val);
			save();
		}

		function exportIt(request) {
			var value = {
				version: lib.getDataVersion(),
				str: lib.getDataVersion() +'\n'
			};

			if (request.config) {
				value.config = settings.get_data();
				value.quickbar = quickbar.get_data();
				value.str += 'config='+ JSON.stringify(settings.get_data()) +'\n' +
					'quickbar='+ JSON.stringify(quickbar.get_data()) +'\n';
			}

			if (request.templates) {
				value.templates = templates.get_data();
				value.str += 'templates='+ JSON.stringify(templates.get_data()) +'\n';
			}

			if (request.reports_villages_coords) {
				value.reports = reports.get_data();
				value.villages = villages.get_data();
				value.coords = villages.get_coords();
				value.str += 'reports='+ JSON.stringify(reports.get_data())  +'\n' +
					'villages='+ JSON.stringify(villages.get_data())   +'\n' +
					'coords='+ JSON.stringify(villages.get_coords())  +'\n';
			}

			if (request.commands) {
				value.commands = commands.get_data();
				value.str += 'commands='+ JSON.stringify(commands.get_data()) +'\n';
			}

			if (request.booty) {
				value.booty = booty.get_data();
				value.str += 'booty='+ JSON.stringify(booty.get_data()) +'\n';
			}

			if (request.misc) {
				value.misc = get_data();
				value.str += 'misc='+ JSON.stringify(get_data()) +'\n';
			}

			return value;
		}
		function importIt(responseData) {
			var values = responseData.split('\n');
			var ret = {
				success: true,
				empty: true
			};
			var i = 0;
			var import_data_versionNum = 0;
			if (/^[\d\.]+$/.test(values[0])) {
				import_data_versionNum = parseFloat(values[0], 10);
				// in the first line is only the version number -> skip that line
				i = 1;
			}
			for (var max = values.length; i < max; i++) {
				if (!values[i])
					continue;

				var type, value, parsedValue;
				var data_versionNum = import_data_versionNum;
				try {
					var splits = values[i].split('=');
					type = splits[0];
					value = values[i].replace(type +'=', '');
					parsedValue = JSON.parse(value);
					// to make it possible to import from the "DS-Farmassistent"
					var match = type.match(/(\w+)\[([\d\.]+)\]/);
					if (match) {
						type = match[1];
						data_versionNum = parseFloat(match[2]);
					}
				} catch(error) {
					lib.debug.log(values[i]);
					if (type)
						ret[type] = false;
					ret.success = false;
					continue;
				}
				if (data_versionNum < 3.8) {
					lib.notifyWrongDataVersion();
					return;
				}

				if (type == 'config') settings.update(data_versionNum, parsedValue);
				if (type == 'quickbar') quickbar.update(data_versionNum, parsedValue);
				if (type == 'templates' || type == 'variants') templates.update(data_versionNum, parsedValue);
				if (type == 'reports') reports.update(data_versionNum, parsedValue);
				if (type == 'villages') villages.update(data_versionNum, parsedValue);
				if (type == 'coords') villages.update(data_versionNum, undefined, parsedValue);
				if (type == 'commands') commands.update(data_versionNum, parsedValue);
				if (type == 'booty') booty.update(data_versionNum, parsedValue);
				if (type == 'misc') update(data_versionNum, parsedValue);

				ret[type] = true;
				ret.empty = false;
			}
			return ret;
		}
		function resetIt(request) {
			if (request.config) {
				settings.reset();
				quickbar.reset();
			}

			if (request.templates)
				templates.reset();

			if (request.reports_villages_coords) {
				reports.reset();
				villages.reset();
			}

			if (request.commands)
				commands.reset();

			if (request.booty)
				booty.reset();

			if (request.misc)
				reset();
		}

		function get_data() {
			return _data;
		}
		function reset() {
			_data = {};
			save(true);
		}
		function update(data_versionNum, data) {
			var change = data ? true : false;
			if (data) _data = clone(data, true);

			// Update
			if (data_versionNum < 4.4) {
				var els_order = lib.storage.getValue('report_els_order');
				if (typeof els_order == 'object') {
					set('report_els_order', els_order);
					change = true;
				}
				var els_enabled = lib.storage.getValue('report_els_enabled');
				if (typeof els_enabled == 'object') {
					set('report_els_enabled', els_enabled);
					change = true;
				}
			}
			if (data_versionNum < 5.3) {
				var values = {
					'bqw': true,
					'tqb': true,
					'bqs': true,
					'tqg': true
				};
				for (var queueKey in values) {
					var countPro = 0;
					for (var villageId in values[queueKey])
						countPro += (values[queueKey][villageId] ? 1 : -1);
					_data[queueKey] = (countPro < 0 ? false : true);
				}
				change = true;
			}
			if (data_versionNum < 5.4) {
				set('map_popup_options', 'buildTemplate', settings.get('template_buildings_enabled'));
				set('map_popup_options', 'unitTemplate', settings.get('template_units_enabled'));
				change = true;
			}

			save(change);
			return true;
		}

		// Update der Daten
		if (texts && data_version != '0' && data_version != lib.getDataVersion()) {
			lib.alert(texts.gui.update.replace('%oldVersion%', data_version).replace('%newVersion%', lib.getDataVersion()));
			oldVersionNum = data_versionNum;
		}
		lib.storage.setValue('data_version', lib.getDataVersion());

		// Die Einstellungen
		settings = (function () {
			/*
			 * Abbreviation:
			 *  1. Letter: Letter of the 'section' (e.g. 'f' for 'Farming' or 'c' for 'misC')
			 *  n. Letter: first letter of that setting, or anything to identify it
			 *    e.g.: m for minRes and ma for maxAge
			 */
			var _changed = false;
			var _idx = {
				consider_successratio: 'gc',
				sitting_enabled: 'ge',
				storageColor_enabledAt: 'ges',
				storageColors: 'gs',
				farmlist_option: 'ff',
				farmlist_attackedFarms: 'fa',
				farming_farmSpys: 'ffs',
				farming_spyOnlyWithUnits: 'fs',
				farming_sendRams: 'fsr',
				farming_minRes: 'fm',
				farming_maxDistance: 'fmd',
				farming_maxAge: 'fma',
				farming_maxRuntime: 'fmr',
				farming_minPop: 'fmp',
				bot_enabled: 'fba',
				bot_farmOver: 'ffo',
				bot_fa_click: 'ffa',
				place_showUnitSelection: 'fc',
				farming_calcUnitNumbers: 'fun',
				farming_unitPreferences_order: 'fuo',
				farming_unitPreferences: 'fu',
				farming_sendCatas: 'fsc',
				farming_buildingPreferences_order: 'fbo',
				farming_buildingPreferences: 'fb',
				map_overlays_enabled: 'mo',
				reports_searchInput: 'rs',
				reports_changeLinkStyle: 'rc',
				reports_newReport: 'rn',
				reports_readReport: 'rr',
				reports_spyReport: 'rsp',
				reports_oldReport: 'ro',
				reports_reorder: 'rro',
				reports_showSurvivor: 'rss',
				reports_showBashPoints: 'rsb',
				freePA: 'pf',
				fileSite: 'pfs',
				showStats: 'ps',
				statSite: 'psi',
				fa_additions_enabled: 'pfa',
				fa_skipAttackedVillages: 'pfk',
				fa_filter_minRes: 'pfm',
				fa_filter_maxDistance: 'pfd',
				hotkeys_enabled: 'ha',
				hotkeys_display: 'hn',
				hotkeys_coordSelector: 'hc',
				template_units_enabled: 'cr',
				template_buildings_enabled: 'cb',
				queue_option: 'cq',
				production_recruitment: 'cp',
				production_removeCols: 'cc',
				production_filter_enabled: 'cpf',
				fixQuickbar: 'cf',
				fixInfobar: 'cfi',
				overview_showBooty: 'co',
				confirm_deleting: 'ce',
				useEnhancedAnimations: 'uea',
				farmlistState: '_s',
				settings_tab: '_st',
				settings_advanced: '_sa'
			};
			var _getStd = function() {
				return {
					consider_successratio: false,
					sitting_enabled: false,
					storageColor_enabledAt: {
						infobar: true,
						production: true,
						market: true,
						place: true,
						map: true,
						info_village: true,
						coin_mint: true
					},
					storageColors: {
						0: [0, 255, 0],
						75: [255, 255, 0],
						100: [255, 0, 0]
					},
					farmlist_option: 2,
					farmlist_attackedFarms: 2,
					farming_farmSpys: [true, 1],
					farming_spyOnlyWithUnits: true,
					farming_sendRams: [true, 1],
					farming_minRes: [false, 500],
					farming_maxDistance: [false, 20],
					farming_maxAge: [false, 36],
					farming_maxRuntime: [false, 3],
					farming_minPop: [false, 30],
					bot_enabled: false,
					bot_farmOver: 0,
					bot_fa_click: 0,
					place_showUnitSelection: true,
					farming_calcUnitNumbers: 0,
					farming_unitPreferences_order: ['light', 'spear', 'marcher', 'heavy', 'sword', 'axe', 'archer', 'knight'],
					farming_unitPreferences: {
						spear: [true, 0],
						sword: [true, 0],
						axe: [true, 0],
						archer: [true, 0],
						light: [true, 0],
						marcher: [true, 0],
						heavy: [true, 0],
						knight: [false, 0]
					},
					farming_sendCatas: false,
					farming_buildingPreferences_order: ['wall', 'farm', 'main', 'barracks', 'stable', 'garage', 'snob', 'smith', 'place', 'statue', 'market', 'wood', 'stone', 'iron', 'storage', 'church_f', 'church'],
					farming_buildingPreferences: {
						main: true,
						barracks: true,
						stable: true,
						garage: true,
						church: false,
						church_f: false,
						snob: true,
						smith: true,
						place: true,
						statue: true,
						market: true,
						wood: false,
						stone: false,
						iron: false,
						farm: true,
						storage: false,
						wall: true
					},
					map_overlays_enabled: true,
					reports_searchInput: true,
					reports_changeLinkStyle: true,
					reports_newReport: 'red',
					reports_readReport: 'green',
					reports_spyReport: 'green',
					reports_oldReport: 'yellow',
					reports_reorder: true,
					reports_showSurvivor: true,
					reports_showBashPoints: true,
					freePA: true,
					fileSite: 0,
					showStats: true,
					statSite: 0,
					fa_additions_enabled: true,
					fa_skipAttackedVillages: true,
					fa_filter_minRes: [false, 500],
					fa_filter_maxDistance: [false, 20],
					hotkeys_enabled: true,
					hotkeys_display: true,
					hotkeys_coordSelector: true,
					template_units_enabled: true,
					template_buildings_enabled: true,
					queue_option: 2,
					production_recruitment: true,
					production_removeCols: true,
					production_filter_enabled: true,
					fixQuickbar: false,
					fixInfobar: false,
					overview_showBooty: true,
					confirm_deleting: true,
					useEnhancedAnimations: false,
					farmlistState: 0,
					settings_tab: 'general',
					settings_advanced: false
				};
			};
			var _std = _getStd();
			var _data = load();

			// public methods
			function load() { return _data = lib.storage.getValue('config_data', {}); }
			function get(key, sub)
			{
				if (_idx.hasOwnProperty(key))
				{
					var k = _idx[key];
					if (_data.hasOwnProperty(k))
					{
						if (sub !== undefined) {
							if (_data[k].hasOwnProperty(sub))
								return _data[k][sub];
							if (_std[key].hasOwnProperty(sub))
								return getStd(key)[sub];
						}
						else
						{
							return _data[k];
						}
					}
					else
					{
						if (sub !== undefined)
						{
							if (_std[key].hasOwnProperty(sub))
								return getStd(key)[sub];
						}
						else
						{
							return getStd(key);
						}
					}
				}
				return undefined;
			}
			function set(key, sub, value)
			{
				if (value === undefined)
				{
					value = sub;
					sub = undefined;
				}
				if (_idx.hasOwnProperty(key))
				{
					var k = _idx[key];
					if (sub !== undefined)
					{
						if (_std.hasOwnProperty(key))
						{
							if (!_data.hasOwnProperty(k))
								_data[k] = getStd(key);
							_data[k][sub] = value;
							_changed = true;
						}
					}
					else
					{
						_data[k] = value;
						_changed = true;
					}
				}
			}
			function save(c) {
				if (c === true || _changed) {
					lib.storage.setValue('config_data', _data);
					_changed = false;
					return true;
				}
				return false;
			}
			function getStd(key) {
				return _getStd()[key];
			}

			function get_data() {
				return _data;
			}
			function reset() {
				_data = {};
				save(true);
			}
			function update(data_versionNum, config_data) {
				var change = config_data ? true : false;
				if (config_data) _data = clone(config_data, true);

				// Update
				if (data_versionNum < 4.0) {
					var oldData = _data;
					_data = {};
					for (var key in oldData) {
						var dat = oldData[key];
						if (typeof(dat) == 'object') {
							dat = clone(oldData[key], true);
						}
						// transform old key into new key:
						if (key == 'place_farmlistOpt')
							key = 'farmlist_option';
						else if (key == 'place_minRes')
							key = 'farming_minRes';
						else if (key == 'place_maxDistance')
							key = 'farming_maxDistance';
						else if (key == 'place_maxAge')
							key = 'farming_maxAge';
						else if (key == 'autoFarm_maxRuntime')
							key = 'farming_maxRuntime';
						else if (key == 'autoFarm_minPop')
							key = 'farming_minPop';
						else if (key == 'place_attackedFarms')
							key = 'farmlist_attackedFarms';
						else if (key == 'place_farmSpys')
							key = 'farming_farmSpys';
						else if (key == 'place_spyOnlyWithUnits')
							key = 'farming_spyOnlyWithUnits';
						else if (key == 'place_sendRams')
							key = 'farming_sendRams';
						else if (key == 'place_checksActive')
							key = 'place_showUnitSelection';
						else if (key == 'autoFarm_active')
							key = 'bot_enabled';
						else if (key == 'autoFarm_farmOver')
							key = 'bot_farmOver';
						else if (key == 'autoFarm_farm_Assistent_click')
							key = 'bot_fa_click';
						else if (key == 'autoFarm_farmWith')
							key = 'farming_calcUnitNumbers';
						else if (key == 'place_unitPreferences_order')
							key = 'farming_unitPreferences_order';
						else if (key == 'place_unitPreferences')
							key = 'farming_unitPreferences';
						else if (key == 'place_sendCatas')
							key = 'farming_sendCatas';
						else if (key == 'place_buildingPreferences_order')
							key = 'farming_buildingPreferences_order';
						else if (key == 'place_buildingPreferences')
							key = 'farming_buildingPreferences';
						set(key, dat);
					}
					delete _data['rsr'];
					change = true;
				}
				if (data_versionNum < 4.3) {
					var units = _data['pu'] || {};
					for (var unit in units) {
						_data['pu'][unit] = [units[unit], _data['au'][unit] || 0];
					}
					// autoFarm_units_order
					delete _data['auo'];
					// autoFarm_units
					delete _data['au'];
					change = true;
				}
				if (data_versionNum < 5.1 && data_versionNum > 4.0) {
					// update this shit!
					var idxChange = {
						'pf': 'ff',
						'pm': 'fm',
						'pma': 'fmd',
						'pmaa': 'fma',
						'amr': 'fmr',
						'amp': 'fmp',
						'pa': 'fa',
						'pfs': 'ffs',
						'ps': 'fs',
						'pse': 'fsr',
						'pc': 'fc',
						'aa': 'fba',
						'af': 'ffo',
						'afa': 'ffa',
						'afw': 'ffw',
						'puo': 'fuo',
						'pu': 'fu',
						'psc': 'fsc',
						'pbo': 'fbo',
						'pb': 'fb'
					};
					for (var oldIdx in idxChange) {
						_data[idxChange[oldIdx]] = _data[oldIdx];
						delete _data[oldIdx];
					}
					var settings_tab = get('settings_tab');
					if (settings_tab == 'place' || settings_tab == 'autoFarm') {
						set('settings_tab', 'farming');
					}
					change = true;
				}
				if (data_versionNum < 5.4) {
					if (_data.hasOwnProperty('mi')) {
						miscSet('map_popup_options', 'farmInfos', _data['mi']);
						delete _data['mi'];
						change = true;
					}
				}
				if (data_versionNum < 5.5) {
					if (_data.hasOwnProperty('ffw'))
					{
						_data['fun'] = _data['ffw'];
						change = true;
					}
				}

				save(change);
				return true;
			}

			if (oldVersionNum && !update(oldVersionNum))
				throw new Error('Dataversion: '+ data_version);

			// settings
			return {
				load: load,
				get: get,
				set: set,
				save: save,
				getStd: getStd,
				// Import/Export/Reset:
				get_data: get_data,
				reset: reset,
				update: update
			};
		})();

		// add functions for own villages! (own)
		villages = (function () {
			var _changed = false;
			var _idx = {
				coords: 0,
				name: 1,
				ownerID: 2,
				spyReportID: 3,
				spyReportTime: 4,
				lastReportID: 5,
				lastReportTime: 6,
				units: 7,
				specialUnits: 8,
				buildings: 9,
				wood: 10,
				stone: 11,
				iron: 12,
				mood: 13,
				bonus: 14,
				eventUntil: 15,
				eq: 16,
				eqCount: 17,
				locked: 18
			};
			var _getStd = function() {
				return ['', '', 0, 0, 0, 0, 0, {}, {}, {}, 0, 0, 0, 100, 0, 0, 1, 0, 0];
			};
			var _unitIdx = {
				spear: 's',
				sword: 'w',
				axe: 'a',
				archer: 'b',
				spy: 'y',
				light: 'l',
				marcher: 'm',
				heavy: 'h',
				ram: 'r',
				catapult: 'c',
				knight: 'k',
				snob: 'n',
				militia: 'i'
			};
			var _buildIdx = {
				main: 'm',
				barracks: 'b',
				stable: 's',
				garage: 'g',
				church: 'c',
				church_f: '_',
				snob: 'a',
				smith: 't',
				place: 'r',
				statue: 'u',
				market: 'k',
				wood: 'w',
				stone: 'o',
				iron: 'i',
				farm: 'f',
				storage: 'e',
				hide: 'h',
				wall: 'l'
			};
			var _data, _coords, _own;
			load();
			var zeroFunction = function() { return 0; };

			var _getMining = (function () {
				if (!lib || !lib.serverConfig || !lib.serverConfig.game.base_config)
					return zeroFunction;

				var k = 30;
				if (lib.serverConfig.game.base_config == 1) k = 15;

				var b = 1.163118;
				if (lib.serverConfig.game.base_config == 1) b = 1.1849979;
				else if (lib.serverConfig.game.base_config == 3) b = 1.149999;

				return function (ID, mine, bonus) {
					var level = getBuilding(ID, mine);
					var ret = !level ? 5 : Math.round(k * Math.pow(b, level-1));
					ret *= lib.serverConfig.speed;
					if (bonus == lib.boni[mine]) ret *= 2;
					else if (bonus == lib.boni.all) ret *= 1.3;
					return Math.round(ret);
				};
			})();
			var _getStorageSize = (function () {
				if (!lib || !lib.serverConfig || !lib.serverConfig.game.base_config)
					return zeroFunction;

				var k = 1.23;
				if (lib.serverConfig.game.base_config > 3) k = 1.2294934;

				return function (ID, bonus) {
					var level = getBuilding(ID, 'storage');
					var ret = 1000 * Math.pow(k, level-1);
					if (bonus == lib.boni.storage) ret *= 1.5;
					return Math.round(ret);
				};
			})();
			var _getHideSize = (function () {
				if (!lib || !lib.serverConfig || !lib.serverConfig.game.base_config)
					return zeroFunction;

				var k = 100;
				var b = 1.3511;
				if (lib.serverConfig.game.base_config > 3) {
					k = 150;
					b = 1.3335;
				}

				return function (ID) {
					var level = getBuilding(ID, 'hide');
					var ret = !level ? 0 : Math.round(k * Math.pow(b, level-1));
					return ret;
				};
			})();
			var _getMaxPop = (function () {
				if (!lib || !lib.serverConfig || !lib.serverConfig.game.base_config)
					return zeroFunction;

				var b = 1.17;
				if (lib.serverConfig.game.base_config > 3) b = 1.1721024;

				return function (ID, bonus) {
					var level = getBuilding(ID, 'farm');
					var ret = !level ? 0 : Math.round(240 * Math.pow(b, level-1));
					if (bonus == lib.boni.farm) ret *= 1.1;
					return ret;
				};
			})();


			// Getter
			function _get(ID, key) {
				var idx = _idx[key];
				if (!_data.hasOwnProperty(ID)) {
					_data[ID] = _getStd();
				} else if (_data[ID].length <= idx) {
					for (var i = _data[ID].length; i <= idx; i++)
						_data[ID][i] = _getStd()[i];
				}
				return _data[ID][idx];
			}
			function getID(idx) {
				return _coords[idx] || 0;
			}
			function getCoords(ID, cPart) {
				var coords = _get(ID, 'coords');
				if (cPart) {
					var split = coords.split('|');
					if (cPart == 'x') return split[0];
					if (cPart == 'y') return split[1];
				}
				return coords;
			}
			function getName(ID) {
				var name = _get(ID, 'name');
				if (typeof(name) != 'string')
					name = name ? texts.gui.bonusVillage : texts.gui.barbarianVillage;
				return name;
			}
			function getOwnerID(ID) {
				return _get(ID, 'ownerID');
			}
			function isBarbarian(ID) {
				var ownerID = _get(ID, 'ownerID');
				return !ownerID;
			}
			function getSpyReportID(ID) {
				return _get(ID, 'spyReportID');
			}
			function getSpyReportTime(ID) {
				return _get(ID, 'spyReportTime');
			}
			function getSpyDate(ID, str) {
				return lib.formatTime(getSpyReportTime(ID) * 1000, str);
			}
			function existSpy(ID) {
				return getSpyReportTime(ID);
			}
			// getLastReportNum:
			function getLastReportID(ID) {
				return _get(ID, 'lastReportID');
			}
			function getLastReportTime(ID) {
				return _get(ID, 'lastReportTime');
			}
			function getLastDate(ID, str) {
				return lib.formatTime(getLastReportTime(ID) * 1000, str);
			}
			function getLastTimeDiff(ID, time) {
				time = time || lib.getTime();
				return !_data.hasOwnProperty(ID) ? Number.MAX_VALUE : (time - getLastReportTime(ID));
			}
			function exist(ID) {
				return getLastReportTime(ID);
			}
			function getUnit(ID, unit) {
				var idx = _unitIdx[unit];
				if (idx) {
					var units = _get(ID, 'units');
					return units[idx] || 0;
				}
				return _get(ID, 'specialUnits')[unit] || 0;
			}
			function getSpecialUnits(ID) {
				return _get(ID, 'specialUnits');
			}
			function noUnits(ID) {
				if (!_data.hasOwnProperty(ID)) return false;
				return !Object.keys(_get(ID, 'units')).length;
			}
			function getBuilding(ID, build) {
				var idx = _buildIdx[build];
				var buildings = _get(ID, 'buildings');
				return buildings[idx] || 0;
			}
			function hasBuildings(ID) {
				return Object.keys(_get(ID, 'buildings')).length;
			}
			//Data.villages.getRes(this.targetID, this.time, false);
			function getRes(ID, time, eq, hide) {
				// no spy report
				if (!getSpyReportTime(ID)) return {};

				time = time || lib.getTime();
				// default: use setting
				eq = eq || settings.get('consider_successratio');
				var hideSize = hide === false ? 0 : _getHideSize(ID);
				var EQ = eq ? getEQ(ID) : 1;

				var timeDiff = (time - getLastReportTime(ID)) / 3600;
				var bonus = getBonus(ID);
				var max = Math.max(_getStorageSize(ID, bonus) - hideSize, 0);
				var wood  = Math.min(Math.round((_get(ID, 'wood')  + timeDiff * _getMining(ID, 'wood', bonus))  * EQ), max);
				var stone = Math.min(Math.round((_get(ID, 'stone') + timeDiff * _getMining(ID, 'stone', bonus)) * EQ), max);
				var iron  = Math.min(Math.round((_get(ID, 'iron')  + timeDiff * _getMining(ID, 'iron', bonus))  * EQ), max);
				var sum = wood + stone + iron;
				return {
					wood: wood,
					stone: stone,
					iron: iron,
					max: max,
					sum: sum
				};
			}
			function getResProd(ID) {
				var bonus = _get(ID, 'bonus');
				var wood  = _getMining(ID, 'wood',  bonus);
				var stone = _getMining(ID, 'stone', bonus);
				var iron  = _getMining(ID, 'iron',  bonus);
				return {
					wood:  wood,
					stone: stone,
					iron:  iron,
					sum: wood + stone + iron
				};
			}
			function getResWarn(ID, time, eq, proz, hide, res) {
				var res = res || getRes(ID, time, eq, hide);
				proz = proz || 1;
				return {
					wood:  (res.wood  >= res.max * proz),
					stone: (res.stone >= res.max * proz),
					iron:  (res.iron  >= res.max * proz),
					sum: (res.sum >= 3 * res.max * proz)
				};
			}
			function getMood(ID, time, refTime, mood) {
				time = time || lib.getTime();
				refTime = refTime || _get(ID, 'lastReportTime');
				mood = mood || 100-_get(ID, 'mood');
				var timeDiff = (time - refTime);
				return Math.min(Math.floor(timeDiff / 3600 * lib.serverConfig.speed) + mood, 100);
			}
			function getBonus(ID) {
				return _get(ID, 'bonus');
			}
			function getEventUntil(ID) {
				return _get(ID, 'eventUntil');
			}
			function getEQ(ID) {
				return _get(ID, 'eq');
			}
			function getEQCount(ID) {
				return _get(ID, 'eqCount');
			}
			function isLocked(ID) {
				return _get(ID, 'locked');
			}

			// belongs anywhere else but not here:
			function getPoints(ID) {
				var points = 0;
				for (var build in lib.buildingInfo) {
					var level = getBuilding(ID, build);
					if (level)
						points += lib.getPoints(build, level);
				}
				return points;
			}
			// TODO: remove not necessary loops!
			function list(locked) {
				var ret = [];
				for (var ID in _data) {
					if (locked && _get(ID, 'locked') || !locked && !_get(ID, 'locked'))
						ret.push(ID);
				}
				return ret;
			}
			function listAll() {
				var ret = [];
				for (var ID in _data)
					ret.push(ID);
				return ret;
			}

			// Setter
			function _set(ID, key, value, sub) {
				if (!_data.hasOwnProperty(ID)) _data[ID] = _getStd();
				if (sub !== undefined) {
					_data[ID][_idx[key]][sub] = value;
				} else {
					_data[ID][_idx[key]] = value;
				}
				_changed = true;
			}
			function setCoords(ID, val) {
				if (!_coords.hasOwnProperty(val)) {
					_coords[val] = ID;
					_changed = true;
				}
				_set(ID, 'coords', val);
			}
			function setName(ID, val) {
				if (!val || val == texts.gui.barbarianVillage)
					val = 0;
				else if (val == texts.gui.bonusVillage)
					val = 1;
				_set(ID, 'name', val);
			}
			function setOwnerID(ID, val) {
				_set(ID, 'ownerID', val);
			}
			function setUnknownOwner(ID) {
				_set(ID, 'ownerID', -1);
			}
			function setSpyReportID(ID, val) {
				_set(ID, 'spyReportID', val);
			}
			function setSpyReportTime(ID, val) {
				_set(ID, 'spyReportTime', val);
			}
			function setLastReportID(ID, val) {
				_set(ID, 'lastReportID', val);
			}
			function setLastReportTime(ID, val) {
				_set(ID, 'lastReportTime', val);
			}
			function setUnit(ID, unit, val) {
				var idx = _unitIdx[unit];
				if (idx) {
					if (!val) {
						var units = _get(ID, 'units');
						delete units[idx];
						_set(ID, 'units', units);
					} else {
						_set(ID, 'units', val, idx);
					}
				} else {
					_set(ID, 'specialUnits', val, unit);
				}
			}
			function setSpecialUnits(ID, val) {
				_set(ID, 'specialUnits', val);
			}
			function setBuilding(ID, build, val) {
				var idx = _buildIdx[build];
				if (idx) {
					if (!val) {
						var buildings = _get(ID, 'buildings');
						delete buildings[idx];
						_set(ID, 'buildings', buildings);
					} else {
						_set(ID, 'buildings', val, idx);
					}
				}
			}
			function setWood(ID, val) {
				_set(ID, 'wood', val);
			}
			function setStone(ID, val) {
				_set(ID, 'stone', val);
			}
			function setIron(ID, val) {
				_set(ID, 'iron', val);
			}
			function setMood(ID, val) {
				_set(ID, 'mood', 100-val);
			}
			function setBonus(ID, val) {
				if (typeof(val) == 'string')
					val = lib.boni[val] || 0;
				_set(ID, 'bonus', val);
			}
			function setEventUntil(ID, val) {
				_set(ID, 'eventUntil', val);
			}
			function setEQ(ID, val) {
				// round to 4 numbers after the comma and restrict to limits (10% - 100%):
				var factor = Math.pow(10, 4);
				val = Math.min(Math.max(Math.round(val * factor) / factor, 0.1), 1);
				_set(ID, 'eq', val);
			}
			function setEQCount(ID, val) {
				_set(ID, 'eqCount', val);
			}
			function setLocked(ID, val) {
				_set(ID, 'locked', val ? 1 : 0);
			}

			function clearUnits(ID) {
				_set(ID, 'units', {});
			}
			function recalcEQ(ID, resExpected, attResultTotal) {
				// do some magic here:
				var resActual = getRes(ID, _get(ID, 'lastReportTime'), false);
				if (resExpected !== false && resActual !== false && resExpected.sum) {
					var EQCount = getEQCount(ID);
					var EQ = getEQ(ID);
					var newEQ = (EQ * EQCount + ((attResultTotal + resActual.sum) / resExpected.sum)) / (EQCount+1);
					setEQ(ID, newEQ);
					setEQCount(ID, EQCount+1);
				}
				_changed = true;
			}
			// Command und Report
			function delData(ID) {
				if (!_data.hasOwnProperty(ID)) return;
				var tos = Data.commands.getTo(ID);
				for (var i = 0, max = tos.length; i < max; i++) {
					var actionID = tos[i];
					var dat = Data.commands.get(actionID);
					if (dat) {
						var fromID = dat.from;
						var froms = Data.commands.getFrom(fromID);
						var pos = froms.length;
						for (var j = 0; j < pos; j++) {
							if (froms[j] == actionID) {
								Data.commands.spliceFrom(fromID);
								pos = j;
							}
						}
						Data.commands.del(actionID);
					}
				}
				Data.commands.delTo(ID);

				reports.remove(getSpyReportID(ID));
				reports.remove(getLastReportID(ID));
				delete _data[ID];
				save(true);
			}

			function load() {
				_data = lib.storage.getValue('village_data', {});
				_coords = lib.storage.getValue('village_coords', {});
				_own = lib.storage.getValue('ownVillages', []);
			}
			function save(c) {
				if (c === true || _changed) {
					lib.storage.setValue('village_data', _data);
					lib.storage.setValue('village_coords', _coords);
					lib.storage.setValue('ownVillages', _own);
					_changed = false;
					return true;
				}
				return false;
			}
			function get_data() {
				return _data;
			}
			function get_coords() {
				return _coords;
			}
			function reset() {
				_data = {};
				_coords = {};
				save(true);
			}
			function update(data_versionNum, village_data, coords_data) {
				var change = village_data || coords_data ? true : false;
				if (village_data) _data = clone(village_data, true);
				if (coords_data) _coords = clone(coords_data, true);

				// Update
				if (data_versionNum < 4.0) {
					for (var ID in _data) {
						var o = _data[ID];
						for (var key in o) {
							var value = o[key];
							if (key == 'name') setName(ID, value);
							else if (key == 'locked') setLocked(ID, value);
							else {
								if (key == 'coords') setCoords(ID, value);
								set(ID, key, value);
							}
						}
					}
					change = true;
				} else if (data_versionNum < 4.1) {
					for (var ID in _data) {
						if (is_array(_data[ID])) {
							if (_data[ID].length == 19) {
								_data[ID].splice(18, 1);
							}
							// insert objekt for "specialUnits"
							_data[ID].splice(8, 0, {});
							// "isLocked" conversion from boolean to integer (0 or 1)
							_data[ID][18] = _data[ID][18] ? 1 : 0;

							var units = _data[ID][7];
							// delete unnecessary information:
							for (var unit in units) {
								if (!units[unit])
									delete units[unit];
							}
						}
					}
					change = true;
				} else if (data_versionNum < 4.5) {
					var offset = (lib.timezoneOffsetHours - (new Date().getTimezoneOffset() / 60)) * 3600;
					for (var ID in _data) {
						var spyTime = get(ID, 'spyReportTime');
						if (spyTime) {
							spyTime -= offset;
							set(ID, 'spyReportTime', spyTime);
						}
						var time = get(ID, 'lastReportTime');
						if (time) {
							time -= offset;
							set(ID, 'lastReportTime', time);
						}
						if (!get(ID, 'locked') && !get(ID, 'bonus') && (!get(ID, 'coords') || !time)) {
							delete _data[ID];
						} else {
							setName(ID, get(ID, 'name'));
						}
					}
					change = true;
				}
				if (data_versionNum < 5) {
					var keys = Object.keys;
					for (var ID in _data) {
						var dat = _data[ID];
						if (!dat[0] ||
							!dat[3] && !dat[5] && !keys(dat[7]).length && !keys(dat[8]).length && !keys(dat[9]).length && !dat[10] && !dat[11] && !dat[12] && dat[13] == 100 && !dat[15] && dat[16] == 1 && !dat[17] && !dat[18]
							) {
							delete _data[ID];
						} else {
							// units:
							var units = _get(ID, 'units');
							_set(ID, 'units', {});
							for (var unitKkey in units) {
								setUnit(ID, unitKkey, units[unitKkey]);
							}
							// buildings:
							var buildings = _get(ID, 'buildings');
							_set(ID, 'buildings', {});
							for (var buildKey in buildings) {
								setBuilding(ID, buildKey, buildings[buildKey]);
							}
							// mood (it is just the )
							setMood(ID, _get(ID, 'mood'));
							// eq (for limits):
							setEQ(ID, getEQ(ID));
						}
					}
				}

				save(change);
				return true;
			}

			if (oldVersionNum && !update(oldVersionNum))
				throw new Error('Dataversion: '+ data_version);

			// villages
			return {
				getID: getID,
				getCoords: getCoords,
				getName: getName,
				getOwnerID: getOwnerID,
				isBarbarian: isBarbarian,
				getSpyReportID: getSpyReportID,
				getSpyReportTime: getSpyReportTime,
				getSpyDate: getSpyDate,
				existSpy: existSpy,
				getLastReportID: getLastReportID,
				getLastReportTime: getLastReportTime,
				getLastDate: getLastDate,
				getLastTimeDiff: getLastTimeDiff,
				exist: exist,
				getUnit: getUnit,
				getSpecialUnits: getSpecialUnits,
				noUnits: noUnits,
				getBuilding: getBuilding,
				hasBuildings: hasBuildings,
				getRes: getRes,
				getResProd: getResProd,
				getResWarn: getResWarn,
				getMood: getMood,
				getBonus: getBonus,
				getEventUntil: getEventUntil,
				getEQ: getEQ,
				getEQCount: getEQCount,
				isLocked: isLocked,

				getPoints: getPoints,
				list: list,
				listAll: listAll,

				setCoords: setCoords,
				setName: setName,
				setOwnerID: setOwnerID,
				setUnknownOwner: setUnknownOwner,
				setSpyReportID: setSpyReportID,
				setSpyReportTime: setSpyReportTime,
				setLastReportID: setLastReportID,
				setLastReportTime: setLastReportTime,
				setUnit: setUnit,
				setSpecialUnits: setSpecialUnits,
				setBuilding: setBuilding,
				setWood: setWood,
				setStone: setStone,
				setIron: setIron,
				setMood: setMood,
				setBonus: setBonus,
				setEventUntil: setEventUntil,
				setEQ: setEQ,
				setEQCount: setEQCount,
				setLocked: setLocked,

				clearUnits: clearUnits,
				recalcEQ: recalcEQ,
				delData: delData,

				load: load,
				save: save,
				// Import/Export/Reset:
				get_data: get_data,
				get_coords: get_coords,
				reset: reset,
				update: update
			};
		})();

		reports = (function () {
			var _changed = false;
			var _idx = {
				reportName: 0,
				type: 1,	// types: 0 = new, 1 = normal, 2 = attack, 3 = spy
				old: 2,
				time: 3,
				dot: 4,
				loot: 5
			};
			var _getStd = function() {
				return ['', 0, 0, 0, 0, 0];
			};
			var _dotList = ['', 'blue', 'green', 'yellow', 'red', 'red_blue', 'red_yellow'];
			var _data = load();


			// Getter
			function _get(ID, key) {
				var idx = _idx[key];
				if (!_data.hasOwnProperty(ID)) {
					_data[ID] = _getStd();
				} else if (_data[ID].length <= idx) {
					for (var i = _data[ID].length; i < idx; i++)
						_data[ID][i] = _getStd()[i];
					return (_data[ID][idx] = _getStd()[idx]);
				}
				return _data[ID][idx];
			}
			function getReportName(ID) {
				return _get(ID, 'reportName');
			}
			function getType(ID) {
				if (isNew(ID)) return 'new';
				if (isOld(ID)) return 'old';
				if (_get(ID, 'type') == 3) return 'spy';
				return 'att';
			}
			function isNew(ID) {
				return !_get(ID, 'type');
			}
			function isOld(ID) {
				return _get(ID, 'old');
			}
			function getTime(ID) {
				return _get(ID, 'time');
			}
			function getDot(ID) {
				var dot = _get(ID, 'dot');
				if (typeof(dot) != 'string')
					dot = _dotList[dot];
				return dot;
			}
			function getLoot(ID) {
				return _get(ID, 'loot') ? 1 : 0;
			}

			// Setter
			function _set(ID, key, value) {
				if (!_data.hasOwnProperty(ID)) _data[ID] = _getStd();
				_data[ID][_idx[key]] = value;
				_changed = true;
				return value;
			}
			function setReportName(ID, val) {
				return _set(ID, 'reportName', val);
			}
			function setTypeNew(ID) {
				return _set(ID, 'type', 0);
			}
			function setTypeNormal(ID) {
				return _set(ID, 'type', 1);
			}
			function setTypeAttack(ID) {
				return _set(ID, 'type', 2);
			}
			function setTypeSpy(ID) {
				return _set(ID, 'type', 3);
			}
			function setOld(ID) {
				return _set(ID, 'old', 1);
			}
			function setTime(ID, val) {
				return _set(ID, 'time', val);
			}
			function setDot(ID, val) {
				var idx = 0;
				for (var i = 1, max = _dotList.length; i < max && !idx; i++) {
					if (val == _dotList[i])
						idx = i;
				}
				return _set(ID, 'dot', idx);
			}
			function setLoot(ID, val, max) {
				return _set(ID, 'loot', (max > val || !max) ? 0 : 1);
			}

			function remove(ID) {
				delete _data[ID];
			}

			function load() { return _data = lib.storage.getValue('report_data', {}); }
			function save(c) {
				if (c === true || _changed) {
					lib.storage.setValue('report_data', _data);
					_changed = false;
					return true;
				}
				return false;
			}
			function get_data() {
				return _data;
			}
			function reset() {
				_data = {};
				save(true);

				// delay execution:
				setTimeout(function() {
					bot.set('referenceID', 0);
					bot.save();
				});
			}
			function update(data_versionNum, report_data) {
				var change = report_data ? true : false;
				if (report_data) _data = clone(report_data, true);

				// Update
				if (data_versionNum < 5) {
					// just reset all data...
					reset();
				}

				save(change);
				return true;
			}

			if (oldVersionNum && !update(oldVersionNum))
				throw new Error('Dataversion: '+ data_version);

			// reports
			return {
				getReportName: getReportName,
				getType: getType,
				isNew: isNew,
				isOld: isOld,
				getTime: getTime,
				getDot: getDot,
				getLoot: getLoot,

				setReportName: setReportName,
				setTypeNew: setTypeNew,
				setTypeNormal: setTypeNormal,
				setTypeAttack: setTypeAttack,
				setTypeSpy: setTypeSpy,
				setOld: setOld,
				setTime: setTime,
				setDot: setDot,
				setLoot: setLoot,

				remove: remove,

				load: load,
				save: save,
				// Import/Export/Reset:
				get_data: get_data,
				reset: reset,
				update: update
			};
		})();

		commands = (function () {
			var _changed = false;
			var _getStd = function() {
				return {
					from: {},
					to: {}
				};
			};
			var _data = load();

			// public methods
			function load() { return _data = lib.storage.getValue('command_data', _getStd()); }
			function get(ID, key) {
				if (!key) return (ID && _data[ID] || {});
				if (!_data.hasOwnProperty(ID)) return undefined;
				return _data[ID][key];
			}
			function set(ID, key, value) {
				if (typeof(key) == 'object') {
					for (var i in key) set(ID, i, key[i]);
				} else {
					if (!_data.hasOwnProperty(ID)) _data[ID] = {};
					_data[ID][key] = value;
					_changed = true;
				}
			}
			function save(c) {
				if (c === true || _changed) {
					lib.storage.setValue('command_data', _data);
					_changed = false;
					return true;
				}
				return false;
			}
			function getFrom(ID) {
				return ID && _data.from[ID] || [];
			}
			function getTo(ID) {
				return ID && _data.to[ID] || [];
			}
			function getReturns(ID) {
				// TODO:
				return [];
			}
			function addFrom(ID, actionID) {
				if (!_data.from.hasOwnProperty(ID)) _data.from[ID] = [];
				_data.from[ID].push(actionID);
				_changed = true;
			}
			function addTo(ID, actionID) {
				if (!_data.to.hasOwnProperty(ID)) _data.to[ID] = [];
				_data.to[ID].push(actionID);
				_changed = true;
			}
			function spliceTo(ID, i) {
				_data.to[ID].splice(i, 1);
				_changed = true;
			}
			function spliceFrom(ID, i) {
				_data.from[ID].splice(i, 1);
				_changed = true;
			}
			function emptyFrom(ID) {
				_data.from[ID] = [];
				_changed = true;
			}
			function del(ID) {
				delete _data[ID];
				_changed = true;
			}
			function delTo(ID) {
				delete _data.to[ID];
				_changed = true;
			}

			function get_data() {
				return _data;
			}
			function reset() {
				_data = _getStd();
				save(true);
			}
			function update(data_versionNum, commands_data) {
				var change = commands_data ? true : false;
				if (commands_data) _data = clone(commands_data, true);


				save(change);
				return true;
			}

			if (oldVersionNum && !update(oldVersionNum))
				throw new Error('Dataversion: '+ data_version);

			// commands
			return {
				load: load,
				get: get,
				set: set,
				save: save,
				getFrom: getFrom,
				getTo: getTo,
				getReturns: getReturns,
				addFrom: addFrom,
				addTo: addTo,
				spliceTo: spliceTo,
				spliceFrom: spliceFrom,
				emptyFrom: emptyFrom,
				del: del,
				delTo: delTo,
				get_data: get_data,
				reset: reset,
				update: update
			};
		})();

		booty = (function () {
			var _changed = false;
			var _idx = {
				wood: 0,
				stone: 1,
				iron: 2,
				villCount: 3
			};
			var _getStd = function() {
				return [0, 0, 0, 0];
			};
			var _keyName = 'booty_data_'+ pID;
			var _data = load();

			// public methods
			function load() { return _data = lib.storage.getValue(_keyName, {}); };
			function get(date, key) {
				if (_idx.hasOwnProperty(key)) {
					var k = _idx[key];
					if (!_data.hasOwnProperty(date)) return _getStd()[k];
					if (_data[date].length <= k) {
						for (var i = _data[date].length; i <= k; i++)
							_data[date][i] = _getStd()[i];
					}
					return _data[date][k];
				} else if (_data.hasOwnProperty(date))
					return _data[date];
				return _getStd();
			}
			function set(date, key, value) {
				if (!_idx.hasOwnProperty(key)) return;
				var k = _idx[key];
				if (!_data.hasOwnProperty(date)) _data[date] = _getStd();
				_data[date][k] = value;
				_changed = true;
			}
			function save(c) {
				if (c === true || _changed) {
					lib.storage.setValue(_keyName, _data);
					_changed = false;
					return true;
				}
				return false;
			}
			function add(date, vals) {
				var villCount = get(date, 'villCount');
				set(date, 'villCount', villCount+1);
				for (var key in vals) {
					var val = vals[key];
					if (val && !isNaN(val)) {
						var old = get(date, key);
						set(date, key, old + val);
					}
				}
			}

			function get_data() {
				return _data;
			}
			function reset() {
				_data = {};
				save(true);
			}
			function update(data_versionNum, booty_data) {
				var change = booty_data ? true : false;
				if (booty_data) _data = clone(booty_data, true);

				// Update
				if (data_versionNum < 4.0) {
					for (var key in _data) {
						_data[key] = [_data[key].wood, _data[key].stone, _data[key].iron, 0];
					}
					change = true;
				} else if (data_versionNum < 4.5) {
					// reset if the timezone is not the same:
					var serverTime = Math.floor(lib.getServerTime().getTime() / 1000);
					var localTime = lib.getTime();
					if (serverTime != localTime) {
						_data = {};
					} else {
						old = _data;
						_data = {};
						for (var key in old) {
							var date = new Date(key * 60 * 60 * 24 * 1000);
							var year = date.getFullYear();
							var month = date.getMonth();
							if (month < 10) month = '0'+ month;
							var day = date.getDate();
							if (day < 10) day = '0'+ day;
							var dateKey = parseInt(year.toString().substr(2) + month + day, 10);
							_data[dateKey] = old[key];
						}
					}
					change = true;
				}

				save(change);
				return true;
			}

			if (oldVersionNum && !update(oldVersionNum))
				throw new Error('Dataversion: '+ data_version);

			// booty
			return {
				load: load,
				get: get,
				set: set,
				save: save,
				add: add,
				// Import/Export/Reset:
				get_data: get_data,
				reset: reset,
				update: update
			};
		})();


		templates = (function () {
			var _changed = false;
			var _getStd = function() {
				return {
					build_hide_complete: false,
					build_selected: {},
					build_templates: [],
					unit_hide: false,
					unit_selected: {},
					unit_templates: []
				};
			};
			var _std = _getStd();
			var _data = load();

			// public methods
			function load() { return _data = lib.storage.getValue('template_data', _getStd()); }
			function get(key, sub) {
				if (_std.hasOwnProperty(key)) {
					if (_data.hasOwnProperty(key)) {
						if (sub !== undefined) {
							if (_data[key].hasOwnProperty(sub)) return _data[key][sub];
						} else return _data[key];
					} else {
						if (sub === undefined)  return _getStd()[key];
					}
				}
				return undefined;
			}
			function set(key, sub, value) {
				if (value === undefined) {
					value = sub;
					sub = undefined;
				}
				if (!_std.hasOwnProperty(key)) return undefined;
				if (sub !== undefined) {
					if (!_data.hasOwnProperty(key)) _data[key] = _getStd()[key];
					_data[key][sub] = value;
				} else _data[key] = value;
				_changed = true;
				return value;
			}
			function save(c) {
				if (c === true || _changed) {
					lib.storage.setValue('template_data', _data);
					_changed = false;
					return true;
				}
				return false;
			}
			function getSelected(type, ID) {
				return get(type +'_selected').hasOwnProperty(ID) ? get(type +'_selected', ID) : -1;
			}
			function getOtherType(type) {
				return {
					build: 'unit',
					unit: 'build'
				}[type];
			}
			function getTemplateCount(type) {
				return get(type +'_templates').length;
			}
			function isType(type) {
				if (type == 'unit' || type == 'build') return true;
				return false;
			}
			function addTemplate(type, newTemplate) {
				var templates = get(type +'_templates');
				// Array.push() returns the new length => last element: length-1
				var num = templates.push(newTemplate) - 1;
				set(type +'_templates', templates);
				return num;
			}
			function del(type, num) {
				var key = type +'_selected';
				for (var ID in _data[key]) {
					// is this template selected?
					if (_data[key][ID] == num) set(key, ID, -1);
				}

				// go through all templates of the other type and check if this template is linked anywhere...
				var otherType = getOtherType(type);
				var oTemplates = otherType +'_templates';
				var template = type +'_template';
				key = type +'_template';
				for (var i = 0, max = get(oTemplates).length; i < max; i++) {
					if (_data[oTemplates][i][template] == num) _data[oTemplates][i][template] = -1;
				}

				var templates = type +'_templates';
				_data[templates].splice(num, 1);
				save(true);
			}

			function get_data() {
				return _data;
			}
			function reset() {
				_data = _getStd();
				save(true);
			}
			function update(data_versionNum, template_data) {
				var change = template_data ? true : false;
				if (template_data) _data = clone(template_data, true);

				// Update
				if (data_versionNum < 4.0) {
					change = true;
				}

				save(change);
				return true;
			}

			if (oldVersionNum && !update(oldVersionNum))
				throw new Error('Dataversion: '+ data_version);

			// templates
			return {
				load: load,
				get: get,
				set: set,
				save: save,
				getSelected: getSelected,
				getOtherType: getOtherType,
				getTemplateCount: getTemplateCount,
				isType: isType,
				addTemplate: addTemplate,
				del: del,
				// Import/Export/Reset:
				get_data: get_data,
				reset: reset,
				update: update
			};
		})();

		bot = (function () {
			var _changed = false;
			var _idx = {
				reportMode: 0,
				referenceID: 1,
				limits: 2,
				farmMode: 3,
				ownVillages: 4,
				startVillageID: 5,
				spysAvailable: 6,
				continuesReading: 7,
				hideWarning: 8
			};
			var _getStd = function() {
				return [0, 0, [0, 1], 0, [], 0, 0, 0, 0];
			};
			var _keyName = 'bot_data_'+ pID;
			var _data = load();

			// public methods
			function load() { return _data = lib.storage.getValue(_keyName, _getStd()); }
			function get(key, sub) {
				if (_idx.hasOwnProperty(key)) {
					var k = _idx[key];
					if (!_data.hasOwnProperty(k)) _data[k] = _getStd()[k];
					if (sub !== undefined) return _data[k][sub];
					return _data[k];
				}
				return undefined;
			}
			function set(key, sub, value) {
				if (value === undefined) {
					value = sub;
					sub = undefined;
				}
				if (!_idx.hasOwnProperty(key)) return undefined;
				var k = _idx[key];
				if (typeof(value) == 'boolean') value = value ? 1 : 0;
				if (sub !== undefined) {
					if (!_data.hasOwnProperty(k)) _data[k] = _getStd()[k];
					_data[k][sub] = value;
				} else _data[k] = value;
				_changed = true;
				return value;
			}
			function save(c) {
				if (c === true || _changed) {
					lib.storage.setValue(_keyName, _data);
					_changed = false;
					return true;
				}
				return false;
			}

			function get_data() {
				return _data;
			}
			function reset() {
				_data = _getStd();
				save(true);
			}
			function update(data_versionNum, bot_data) {
				var change = bot_data ? true : false;
				if (bot_data) _data = clone(bot_data, true);

				// Update
				if (data_versionNum < 4.2) {
					_data = _getStd();
					change = true;
				}

				save(change);
				return true;
			}

			if (oldVersionNum && !update(oldVersionNum))
				throw new Error('Dataversion: '+ data_version);

			// bot
			return {
				load: load,
				get: get,
				set: set,
				save: save,
				// Import/Export/Reset:
				get_data: get_data,
				reset: reset,
				update: update
			};
		})();

		hotkeys = (function () {
			var _changed = false;
			var _getStd = function() {
				return {};
			};
			var _std = _getStd();
			var _data = load();

			// public methods
			function load() { return _data = lib.storage.getValue('hotkeys_data', _getStd()); }
			function get(key, sub) {
				if (_std.hasOwnProperty(key)) {
					if (_data.hasOwnProperty(key)) {
						if (sub !== undefined) {
							if (_data[key].hasOwnProperty(sub)) return _data[key][sub];
						} else return _data[key];
					} else {
						if (sub === undefined)  return _getStd()[key];
					}
				}
				return undefined;
			}
			function set(key, sub, value) {
				if (value === undefined) {
					value = sub;
					sub = undefined;
				}
				if (!_std.hasOwnProperty(key)) return undefined;
				if (sub !== undefined) {
					if (!_data.hasOwnProperty(key)) _data[key] = _getStd()[key];
					_data[key][sub] = value;
				} else _data[key] = value;
				_changed = true;
				return value;
			}
			function save(c) {
				if (c === true || _changed) {
					lib.storage.setValue('template_data', _data);
					_changed = false;
					return true;
				}
				return false;
			}

			// hotkeys
			return {
				load: load,
				get: get,
				set: set,
				save: save
			};
		})();

		quickbar = (function () {
			var _changed = false;
			var _idx = {
				name: 0,
				title: 1,
				img: 2,
				target: 3,
				newTab: 4
			};
			var _getStd = function() {
				return [
					[texts.building.main, '', '{graphic}/buildings/main.png', '{game}&screen=main', 0],
					[texts.gui.recruit, '', '{graphic}/buildings/barracks.png', '{game}&screen=train', 0],
					[texts.building.snob, '', '{graphic}/buildings/snob.png', '{game}&screen=snob', 0],
					[texts.building.smith, '', '{graphic}/buildings/smith.png', '{game}&screen=smith', 0],
					[texts.building.place, '', '{graphic}/buildings/place.png', '{game}&screen=place', 0],
					[texts.building.market, '', '{graphic}/buildings/market.png', '{game}&screen=market', 0]
				];
			};
			var _data = load();

			// public methods
			function load() { return _data = lib.storage.getValue('quickbar_data', _getStd()); }
			function _get(i, key) {
				var idx = _idx[key];
				return _data[i][idx];
			}
			function _set(i, key, value) {
				var idx = _idx[key];
				if (typeof(_data[i]) != 'object')
					_data[i] = [];
				_data[i][idx] = value;
				return value;
			}
			function getName(i) {
				return _get(i, 'name');
			}
			function getTitle(i) {
				return _get(i, 'title');
			}
			function getImg(i) {
				return _get(i, 'img');
			}
			function getTarget(i) {
				return _get(i, 'target');
			}
			function isNewTab(i) {
				return _get(i, 'newTab');
			}
			function isLinebreak(i) {
				return _data[i] == 0;
			}
			function getMax() {
				return Object.keys(_data).length;
			}

			function setName(i, value) {
				return _set(i, 'name', value);
			}
			function setTitle(i, value) {
				return _set(i, 'title', value);
			}
			function setImg(i, value) {
				return _set(i, 'img', value);
			}
			function setTarget(i, value) {
				return _set(i, 'target', value);
			}
			function setNewTab(i, value) {
				return _set(i, 'newTab', value ? 1 : 0);
			}
			function setLinebreak(i) {
				_data[i] = 0;
			}
			function clear() {
				_changed = true;
				_data = [];
			}

			function save(c) {
				if (c === true || _changed) {
					lib.storage.setValue('quickbar_data', _data);
					_changed = false;
					return true;
				}
				return false;
			}

			function get_data() {
				return _data;
			}
			function reset() {
				_data = _getStd();
				save(true);
			}
			function update(data_versionNum, quickbar_data) {
				var change = quickbar_data ? true : false;
				if (quickbar_data) _data = clone(quickbar_data, true);

				// Update
				if (data_versionNum < 5.2) {
					var oldDat = lib.storage.getValue('quickbar_items');
					if (oldDat) {
						clear();
						for (var i = 0, max = oldDat.length; i < max; i++) {
							var dat = oldDat[i];
							setName(i, dat.name);
							setTitle(i, dat.title);
							setImg(i, dat.img);
							setTarget(i, dat.link);
							setNewTab(i, dat.newTab);
						}
						lib.storage.deleteValue('quickbar_items');
						change = true;
					}
				}

				save(change);
				return true;
			}

			if (oldVersionNum && !update(oldVersionNum))
				throw new Error('Dataversion: '+ data_version);

			// quickbar
			return {
				getName: getName,
				getTitle: getTitle,
				getImg: getImg,
				getTarget: getTarget,
				isNewTab: isNewTab,
				isLinebreak: isLinebreak,
				getMax: getMax,

				setName: setName,
				setTitle: setTitle,
				setImg: setImg,
				setTarget: setTarget,
				setNewTab: setNewTab,
				setLinebreak: setLinebreak,

				clear: clear,

				load: load,
				save: save,
				// Import/Export/Reset:
				get_data: get_data,
				reset: reset,
				update: update
			};
		})();


		(function() {
			var hidden = 'hidden';

			// Standards:
			if (hidden in document)
				document.addEventListener('visibilitychange', onchange);
			else if ((hidden = 'mozHidden') in document)
				document.addEventListener('mozvisibilitychange', onchange);
			else if ((hidden = 'webkitHidden') in document)
				document.addEventListener('webkitvisibilitychange', onchange);
			else if ((hidden = 'msHidden') in document)
				document.addEventListener('msvisibilitychange', onchange);
			// IE 9 and lower:
			else if ('onfocusin' in document)
				document.onfocusin = document.onfocusout = onchange;
			// All others:
			else
				window.onpageshow = window.onpagehide = window.onfocus = window.onblur = onchange;

			function onchange(evt) {
				var v = 'visible', h = 'hidden';
				var evtMap = {
					focus: v, focusin: v, pageshow: v,
					blur: h, focusout: h, pagehide: h
				};

				evt = evt || window.event;
				var type;
				if (evt.type in evtMap) type = evtMap[evt.type];
				else type = this[hidden] ? h : v;

				if (type == v) {
					// reload data from storage
					load();
					settings.load();
					villages.load();
					reports.load();
					commands.load();
					booty.load();
					templates.load();
					hotkeys.load();
					quickbar.load();
					bot.load();
				}
			}
		})();


		// Data
		return {
			load: load,
			get: get,
			set: set,
			save: save,
			setStd: setStd,
			getQueue: getQueue,
			setQueue: setQueue,
			exportIt: exportIt,
			importIt: importIt,
			resetIt: resetIt,
			// spezielle Daten
			settings: settings,
			villages: villages,
			reports: reports,
			commands: commands,
			booty: booty,
			templates: templates,
			bot: bot,
			hotkeys: hotkeys,
			quickbar: quickbar
		};
	})();


	var Style = (function () {

		var vers = game_data.version.replace(/ [\d\.]+/, '');
		var img_base = image_base.replace(/http:\/\/cdn2?\./, 'http://cdn.').replace(/\/[\d\.]+\/\d+\//, '/'+ game_data.majorVersion +'/'+ vers +'/');
		var left = (lib.lang == 'ae' ? 'right' : 'left');
		var right = (lib.lang == 'ae' ? 'left' : 'right');
		add('basic').set(
			'.preference_table td { padding:1px !important; }' +
			'.preference_table td, .preference_table th { text-align:center; vertical-align:middle; }' +
			'.preference_table .ui-sortable-placeholder { height:19px; }' +
			'div.bqhandle { width:11px; height:11px; background-image:url('+ image_base +'sorthandle.png); cursor:pointer; }' +
			'input.dsplus { border:1px solid #888888; }' +
			'input.dsplus[type="text"]:hover, input.dsplus[type="text"]:focus { border-color:#5555ff !important; background-color:#ffffff !important; }' +
			'input[type="text"].dsplus:focus { box-shadow:0 0 4px rgba(140, 140, 155, 0.8); }' +
			'input[type="checkbox"].dsplus { cursor:pointer; }' +
			'input.dsplus.disabled { background-color:#cfcfcf; border:1px solid #808080; }' +
			'input:checked ~ input.dsplus[type="text"].enabled { background-color:#d6ff5b; border:1px solid #008000; }' +
			'input:not(:checked) ~ input.dsplus[type="text"].enabled { background-color:#ffbc9e; border:1px solid #ff0000; }' +
			'select.dsplus { border:1px solid grey; border-right:none; }' +
			'select.dsplus:hover, select.dsplus:focus { border-color:#5555ff; }' +
			'.btn-bcr, .btn-btr { box-shadow:none; }' +
			'.btn-attack {' +
				'background:url("'+ img_base +'btn/attack.png") no-repeat scroll 5px 2px, linear-gradient(to bottom, #b87e6f 0%, #a8533e 22%, #913a22 30%, #782611 100%) repeat scroll 0 0 transparent;' +
			'}' +
			'.btn-attack:hover {' +
				'background:url("'+ img_base +'btn/attack.png") no-repeat scroll 5px 2px, linear-gradient(to bottom, #d39888 0%, #c97259 22%, #ce4f2c 30%, #9f462d 100%) repeat scroll 0 0 transparent;' +
			'}' +
			'.btn-support {' +
				'background:url("'+ img_base +'btn/support.png") no-repeat scroll 5px 2px, linear-gradient(to bottom, #88b86f 0%, #61a83e 22%, #459122 30%, #347811 100%) repeat scroll 0 0 transparent;' +
			'}' +
			'.btn-support:hover {' +
				'background:url("'+ img_base +'btn/support.png") no-repeat scroll 5px 2px, linear-gradient(to bottom, #94cd79 0%, #71c44a 22%, #57bd28 30%, #498f28 100%) repeat scroll 0 0 transparent;' +
			'}' +
			'.align_left { text-align:'+ left +'; }' +
			'.align_right { text-align:'+ right +'; }'
		);

		function add(name) {
			var newStyle = name && __('dsplusStyle_'+ name);
			if (!newStyle) {
				newStyle = document.createElement('style');
				newStyle.type = 'text/css';
				if (name)
					newStyle.id = 'dsplusStyle_'+ name;
				document.head.appendChild(newStyle);
			}
			function setInnerHTML(css) {
				newStyle.innerHTML = css;
				return newStyle;
			}

			return {
				element: newStyle,
				set: setInnerHTML
			};
		}

		function getBase64(name) {
			return {
				'lock-icon':
					''
			}[name];
		}

		function errorReport() {
			add('errorReport').set(
				'#errorMsg { font-size:14px; position:fixed; bottom:3px; right:16px; z-index:12000; }' +
				'#errorMsg img.quest_arrow { height:71px; left:50%; width:67px; margin-left:-34px; top:-73px; }' +
				'#errorOverlay { background-color:black; opacity:0; position:fixed; top:0px; left:0px; height:100%; width:100%; z-index:14000; display:none; transition:all 0.2s ease-in-out; }' +
				'#errorOverlay.open { display:block; }' +
				'#errorOverlay.fade { opacity:0.7; }' +
				'#errorWindow {' +
					'background-color:#f4e4bc;' +
					'border-radius:4px;' +
					'border:1px solid red;' +
					'box-shadow:0px 0px 5px white;' +
					'display:none;' +
					'opacity:0;' +
					'transform:scale(0.9);' +
					'transition:all 0.2s ease-in-out;' +
					'font-size:14px;' +
					'padding:20px;' +
					'margin-left:-300px;' +
					'width:600px;' +
					'position:fixed;' +
					'left:50%;' +
					'top:100px;' +
					'z-index:15000;' +
				'}' +
				'#errorWindow.open { display:block; }' +
				'#errorWindow.fade { opacity:1; transform:scale(1); }' +
				'#errorWindow textarea { width:calc(100% - 10px); }' +
				'#closeErrorWindow { background-image:url("'+ image_base +'login_close.png"); display:block; width:20px; height:20px; position:absolute; top:15px; right:15px; }'
			);
		}
		function settings(popupWidth, popupHeight, popupTop) {
			var input_enhancedStyle = '';
			var info_popup_enhancedStyle = '';
			if (Data.settings.get('useEnhancedAnimations')) {
				input_enhancedStyle = 'transition:border 0.25s ease-out, background-color 0.25s ease-out;';
				info_popup_enhancedStyle = 'transition:bottom 0.5s, left 0.3s, width 0.3s, height 0.3s;';
			}
			add('settings').set(
				'#dsplus_settings {' +
					'background-image:url("'+ image_base +'index/main_bg.jpg");' +
					'box-shadow:0px 0px 30px #000000;' +
					'border-width:1px; border-style:solid; border-color:#804000 #603000 #402000 #804000; border-radius:5px;' +
					'display:block;' +
					'position:absolute; left:50%; top:'+ popupTop +'px;' +
					'width:'+ popupWidth +'px; height:'+ popupHeight +'px;' +
					'padding:0; margin:-8px 0 0 -375px;' +
					'overflow:hidden; z-index:9998;' +
					'opacity:0;' +
					'transform:scale(0.95);' +
					'transition:opacity 0.3s ease-out 0s, transform 0.3s ease-out;' +
				'}' +
				'#dsplus_settings.open { opacity:1; transform:scale(1); }' +
				'#dsplus_settings.open.immediately { transition:all 0s; }' +
				'#dsplus_settings a { outline:none; }' +
				'#dsplus_settings div.menu_bar { background:#c1a264 url("'+ image_base +'screen/tableheader_bg3.png") repeat-x; border-bottom:1px solid hsl(30, 100%, 25%); padding:2px 5px; height:auto; }' +
				'#dsplus_settings div.menu_bar.popup_menu img { cursor:pointer; }' +
				'#dsplus_settings_main { overflow:auto; background-color:#f4e4bc; width:'+ popupWidth +'px; }' +
				'#dsplus_settings_main td.caption { white-space:nowrap; text-align:right; width:1px; }' +
				'#dsplus_settings_main div.caption { border-bottom:1px dashed rgb(123, 79, 13); background-color:hsla(40, 49%, 67%, 0.4); }' +
				'#dsplus_settings_main .dsplus_option_container { margin-bottom:2px; padding:2px 6px; }' +
				'#dsplus_settings_main .dsplus_option_container:last-child { margin-bottom:0px; }' +
				'input.dsplus ~ span { margin-left:5px; }' +
				'#dsplus_settings_main .dsplus:not(:checked) ~ input[type="text"] { background-color:#cfcfcf; border:1px solid #808080; }' +
				'#dsplus_settings_main span[rel] { cursor:pointer; -webkit-touch-callout:none; -webkit-user-select:none; -khtml-user-select:none; -moz-user-select:none; -ms-user-select:none; user-select:none; }' +
				'#dsplus_settings_main .content-border { margin-bottom:6px; border-left:0; border-right:0; }' +
				'#dsplus_settings_main .content-border:first-child { border-top:0; }' +
				'#dsplus_settings_main .content-border:last-child { margin-bottom:-1px; }' +
				'#dsplus_settings_main table.content-border { width:100%; padding:1px 3px; }' +
				'#dsplus_settings_main table.content-border td { padding:2px 3px; }' +
				'#dsplus_settings_main .advanced { display:none; }' +
				'#dsplus_settings_main.advanced .advanced { display:block; }' +
				'#dsplus_settings_main.advanced table.advanced { display:table; }' +
				'#dsplus_settings_main.advanced tr.advanced { display:table-row; }' +

				'#dsplus_settings_info_popup { background-image:url("'+ image_base +'index/main_bg.jpg");' +
					'border-color:#804000 #603000 #402000 #804000; border-width:1px 1px 0 1px; border-style:solid; border-radius:5px 5px 0 0;' +
					'bottom:25px; position:absolute; min-width:200px; '+ info_popup_enhancedStyle +
				'}' +
				'#dsplus_settings_info_popup .menu_bar { border-radius:4px 4px 0 0; font-weight:bold; cursor:default; }' +
				'#dsplus_settings_info_popup .menu_bar a.close { border-left:1px solid hsl(30, 100%, 25%); display:block; height:100%; left:5px; margin:0 0 0 15px; padding:2px 5px; position:relative; top:-2px; }' +
				'#dsplus_settings_footer { position:absolute; bottom:0px; width:'+ (popupWidth-6) +'px; }' +
				'#dsplusSettingsAdvanced { margin:1px 4px 0px; }' +
				'#dsplusSettingsAdvancedSpan { cursor:pointer; -webkit-touch-callout:none; -webkit-user-select:none; -khtml-user-select:none; -moz-user-select:none; -ms-user-select:none; user-select:none; }' +
				'.settingEnable { cursor:pointer; position:relative; top:2px; }' +
				'#dsplus_reports_tab img.dot { cursor:pointer; border:1px solid transparent; margin:0 1px; height:10px; width:10px; }' +
				'#dsplus_reports_tab img.dot:hover { background-color:#fbf6e9; }' +
				'#dsplus_reports_tab img.dot.selected { box-shadow:0px 0px 2px black; }' +
				'#dsplus_farming_unitPreferences .ui-sortable-placeholder { height:23px; }' +
				'.preference_table table { border-spacing:0; }' +
				'.preference_table table td { background-color:transparent; }' +
				'.preference_table table td img, .preference_table table td input[type="text"] { margin-right:2px; }' +
				'.preference_table table td img { height:18px; width:18px; }' +
				'.preference_table table input { text-align:center; }' +
				'#dsplus_quickbar td { padding:2px 3px; }' +
				'.dsplus.dsplus_hotkey_input { width:80px; }' +
				'.btn-green { background:linear-gradient(to bottom, #0bad00 0%, #0e7b1e 100%) repeat scroll 0 0 transparent; }' +
				'.btn-green:hover { background:linear-gradient(to bottom, #13c600 0%, #129e23 100%) repeat scroll 0 0 transparent; }'
			);
		}
		function groupPopup() {
			add('groupPopup').set(
				'#group_popup { backface-visibility:hidden; border-color:#804000 #603000 #402000 #804000; border-radius:8px 8px 0 0; border-style:solid; border-width:1px;' +
					'opacity:0; position:absolute; transform:scale(0.7); transition:all 0.3s ease 0s; width:298px; z-index:99999; }' +
				'#group_popup.hidden { height:0 !important; width:0 !important; }' +
				'#group_popup.hidden * { display:none; }' +
				'#group_popup.show { opacity:1; transform:scale(1); visibility:visible; }' +
				'#group_popup.ui-draggable-dragging { background-color:#ff0000 !important; transition:none 0s ease 0s !important; }' +
				'#group_popup_menu {' +
					'background:#c1a264 url("'+ image_base +'screen/tableheader_bg3.png") repeat-x;' +
					'border-radius:8px 8px 0 0;' +
					'cursor:move;' +
					'font-size:17px;' +
					'font-weight:bold;' +
					'height:26px;' +
					'padding:2px 5px;' +
					'width:auto;' +
				'}' +
				'#group_popup_title { font-size:17px; font-weight:bold; }' +
				'#group_popup_close { float:'+ right +'; }' +
				'#group_popup_main { background-image:url("'+ image_base +'index/main_bg.jpg"); height:300px; overflow:auto; padding:3px; }'
			);
		}
		function farmlist() {
			add('farmlist').set(
				'#dsplus_farmTable th { white-space:nowrap; }' +
				'#dsplus_farmTable #selectedFarmRow > td { background-color:#fff6dc; }' +
				'#dsplus_farmTable tr td { background-color:#f4e4bc; }' +
				'#dsplus_farmTable tr:hover td { background-color:#fff4cc; }' +
				'#dsplus_farmTable tr.attack td { background-color:#d4c4ac; }' +
				'#dsplus_farmTable tr.attack:hover td { background-color:#dfcfaf; }' +
				'#dsplus_farmTable tr td.first-player { background-image:url("'+ image_base +'icons/header.png"); background-repeat:no-repeat; background-position:-72px 1px; width:12px; }' +
				'#dsplus_farmTable tr .lock-icon { background-image:url("'+ getBase64('lock-icon') +'"); }'
			);
		}
		function buildingRow() {
			add('buildingRow').set(
				'#buildings tr:hover td { background-color:#fff0cc; }' +
				'#buildings_unmet tr:hover td { background-color:#fff0cc; }' +
				'#buildings span.neededResources { color:#ee0000; font-size:8pt; }' +
				'#buildings span.neededResources.inactive { color:#c5a76b; }'
			);
		}
		function template() {
			add('template').set(
				'#unit_templates input[type="text"], #build_templates input[type="text"] { border:1px solid grey; padding:1px 2px; height:15px; }'
			);
		}
		function reportOverview() {
			add('reportOverview').set(
				'#dsplus_search { background:transparent; border:1px solid transparent; padding:2px 3px; font-weight:700; font-size:9pt; width:calc(100% - 8px); transition:all 0.3s; }' +
				'#dsplus_search:hover, #dsplus_search:focus { color:#0000ff; border-color:grey; background-color:rgba(255, 255, 255, 0.5); transition:all 0s; }' +
				'#dsplus_search_delete { position:absolute; top:1px; right:0px; }' +
				'tr.newReport a:first-child { color:#0000bb; }' +
				'tr.oldReport a:first-child { color:#808080; }'
			);
		}
		function report() {
			add('report').set(
				'.container > h4 { margin-top:17px; }'
			);
		}
		function map() {
			add('map').set(
				'div.dsplus_Info_overlay { position:absolute; z-index:10; width:53px; height:38px; }' +
				'div.ovl_points { position:relative; width:53px; height:10px; top:12px; font-size:xx-small; font-weight:bold; color:hsl(0, 0%, 80%); text-align:center; vertical-align:middle; }' +
				'div.ovl_name { position:relative; width:43px; height:38px; left:10px; font-size:xx-small; color:#F0C800; }' +
				'div.ovl_coords { position:relative; width:53px; height:10px; top:12px; font-size:xx-small; font-weight:bold; color:hsl(0, 0%, 80%); text-align:center; vertical-align:middle; }' +
				'div.ovl_eq { position:relative; width:53px; height:10px; top:12px; font-size:xx-small; font-weight:bold; color:#F0C800; text-align:center; vertical-align:middle; }'
			);
		}
		function am_farm() {
			add('am_farm').set(
				'tr.attack > td { background-color:#f0727e !important; }'
			);
		}
		function filter() {
			add('filter').set(
				'#dsplus_filterLink { vertical-align:middle; }'
			);
		}
		function newReportAnimation(width) {
			if (Data.settings.get('useEnhancedAnimations')) {
				add('new_report').set(
					'@keyframes newReportEyeCatcher { from {width:'+ width +'px;} to {width:'+ (width+30) +'px;} }' +
					'#new_reportCell { animation:newReportEyeCatcher 0.2s ease 0.8s 4 alternate; }'
				);
			} else {
				add('new_report').set(
					'@keyframes newReportEyeCatcher { from {opacity:1;} to {opacity:0.1;} }' +
					'#new_reportCell { animation:newReportEyeCatcher 0.4s ease 1s 12 alternate; }'
				);
			}
		}

		return {
			left: left,
			right: right,
			add: add,
			errorReport: errorReport,
			settings: settings,
			groupPopup: groupPopup,
			farmlist: farmlist,
			buildingRow: buildingRow,
			template: template,
			reportOverview: reportOverview,
			report: report,
			map: map,
			am_farm: am_farm,
			filter: filter,
			newReportAnimation: newReportAnimation
		};
	})();


	var GUI = (function (jQuery) {
		var settings, filter, reports, map;

		function loading(id) {
			id = 'dsplus_'+ id;
			var img = document.createElement('img');
			img.src = image_base +'throbber.gif';
			img.id = id;
			img.alt = '';
			img.style.position = 'fixed';
			document.body.appendChild(img);

			img.style.left = Math.floor((document.body.offsetWidth  - img.offsetWidth)  / 2) +'px';
			img.style.top  = Math.floor((document.body.offsetHeight - img.offsetHeight) / 2) +'px';
			return img;
		}

		function hideScriptWarnings() {
			var hideScriptWarnings = Data.get('hideScriptWarnings');
			var script_warning = __('script_warning');

			var HTML = '<span style="display: '+ (!hideScriptWarnings ? 'block' : 'none') +';">'+ texts.gui.fold +'</span>' +
				'<span style="display: '+ ( hideScriptWarnings ? 'block' : 'none') +';">'+ texts.gui.expand +'</span>';
			var toggleLink = _ce('a', HTML, {
				'style': 'margin-right: 5px;',
				'class': 'float_right',
				'href': 'javascript:;'
			});
			toggleLink.addEventListener('click', function() {
				var spans = _T(this, 'span');
				jQuery(spans).toggle();
				hideScriptWarnings = (spans[0].style.display == 'none');

				if (hideScriptWarnings) {
					script_warning.style.height = '8px';
					script_warning.style.overflow = 'hidden';
				} else {
					script_warning.style.height = '';
					script_warning.style.overflow = '';
				}

				Data.set('hideScriptWarnings', hideScriptWarnings);
				Data.save();
			}, true);

			var firstChild = script_warning.firstChild;
			if (firstChild)
				script_warning.insertBefore(toggleLink, firstChild);
			else
				script_warning.appendChild(toggleLink);

			if (hideScriptWarnings) {
				script_warning.style.height = '8px';
				script_warning.style.overflow = 'hidden';
			}
		}

		function fixBar(id) {
			var el = __(id);
			if (el) {
				var dummy = __(id +'_dummy');
				var $dummy = jQuery(dummy);
				if (!dummy) {
					dummy = _ce('div', '', {
						'id': id +'_dummy',
						'style': 'width: 100%; height: '+ el.offsetHeight +'px;'
					});
					_before(dummy, el);
					$dummy = jQuery(dummy);
				}
				el.style.position = 'fixed';
				el.style.width = dummy.offsetWidth +'px';
				el.style.zIndex = 100;

				var dummyOffset = $dummy.offset();
				el.style.left = dummyOffset.left +'px';
				el.style.top = dummyOffset.top +'px';
			}
		}
		function fixQuickbar() {
			fixBar('quickbar_outer');
		}
		function fixInfobar() {
			fixBar('header_info');
		}
		function addPlayerLink() {
			var rank = document.querySelector('div.rank');
			var link = document.createElement('a');
			link.href = game_data.link_base_pure +'info_player';
			link.style.padding = '0px';
			while (rank.childNodes.length) {
				link.appendChild(rank.firstChild);
			}
			rank.appendChild(link);
		}

		function initErrorGUI(data) {
			if (!__('linkContainer'))
				return;

			Style.errorReport();

			var messageDiv = _ce(
				'div',
				'<img class="quest_arrow quest_arrow_down" src="/graphic/quests/ar_down.png">' +
				'<a style="color: red;" href="javascript:;">An error has occurred!</a>',
				{
					'id': 'errorMsg'
				}
			);
			var arrowEl = _T(messageDiv, 'img', 0);
			var counter = 8;
			var interval = setInterval(function() {
				if (!arrowEl.parentNode)
					_prepend(arrowEl, messageDiv);
				if (!--counter)
					clearInterval(interval);
			}, 100);
			doc.body.appendChild(messageDiv);

			createErrorWindow(data);
			var errorOverlay = __('errorOverlay');
			var errorWindow = __('errorWindow');
			messageDiv.addEventListener('click', function() {
				errorOverlay.classList.add('open');
				errorWindow.classList.add('open');
				setTimeout(function() {
					errorOverlay.classList.add('fade');
					errorWindow.classList.add('fade');
				}, 10);
				arrowEl.classList.remove('quest_arrow_down');
			}, true);
			var close = function() {
				errorOverlay.classList.remove('fade');
				errorWindow.classList.remove('fade');
				setTimeout(function() {
					errorOverlay.classList.remove('open');
					errorWindow.classList.remove('open');
				}, 200);
			};
			__('closeErrorWindow').addEventListener('click', close, true);
			__('errorOverlay').addEventListener('click', close, true);
		}
		function createErrorWindow(error) {
			var errorOverlay = _ce('div', '', {
				'id': 'errorOverlay'
			});
			var errorWindow = _ce('div',
				'<h2 style="color: red; text-align: center;">An error has occurred in "'+ texts.gui.title +'"!</h2>' +
				'<textarea readonly="readonly" style="resize: vertical;" rows="20">' +
					'DS+ '+ error.scriptVersion +' for TW v'+ error.forTWVersion +' (build '+ error.forTWBuild +'); data v'+ error.dataVersion +'\r\n' +
					'TW v'+ error.twVersion +' (build '+ error.twBuild +')' +'\r\n' +
					'\r\n' +
					error.name +': '+ error.message +'\r\n' +
					'@ "'+ error.fileName +'": '+ error.lineNumber +'\r\n' +
					'\r\n' +
					'URL:\r\n' +
					'----\r\n' +
					error.url +'\r\n' +
					'\r\n' +
					'Browser:\r\n' +
					'--------\r\n' +
					error.browser +'\r\n' +
					'\r\n' +
					'Stack:\r\n' +
					'------\r\n' +
					error.stack +
				'</textarea>' +
				'<a id="closeErrorWindow" href="javascript:;"></a>',
				{
					'id': 'errorWindow'
				}
			);
			document.body.appendChild(errorOverlay);
			document.body.appendChild(errorWindow);

			var errorTextarea = _T(errorWindow, 'textarea', 0);
			errorTextarea.addEventListener('focus', function() {
				this.select();
			}, false);
			errorTextarea.addEventListener('dblclick', function() {
				this.select();
			}, false);
		}

		settings = (function () {
			function colorInput(el) {
				var checkbox = el.parentNode.querySelector('input[type="checkbox"]');
				var checkboxEnabled = !checkbox.disabled;
				var disabled = /\(0\)/.test(_T(el.parentNode, 'a', 'last').innerHTML);
				var checked = checkbox.checked;
				el.classList.add('dsplus');
				el.classList.remove('disabled');
				el.classList.remove('enabled');
				if (disabled) {
					el.classList.add('disabled');
				} else if (checkboxEnabled) {
					el.classList.add('enabled');
				}
			}
			// creator functions
			function createCheckbox(id, checked) {
				var checkbox = _ce('input', '', {
					'id': id,
					'class': 'dsplus',
					'type': 'checkbox'
				});
				if (checked) checkbox.checked = true;
				return checkbox;
			}
			function createInput(id, value, style, other, classAddition) {
				id = id || '';
				value = value || '';
				style = style || '';
				other = other || '';
				classAddition = classAddition || '';
				return '<input class="dsplus '+ classAddition +'" type="text" id="'+ id +'" value="'+ value +'" style="'+ style +'" '+ other +'>';
			}
			function createInputEl(id, value, attributes) {
				var input = _ce('input', '', {
					'id': id,
					'class': 'dsplus',
					'type': 'text',
					'value': value || ''
				});
				if (attributes) {
					for (var attr in attributes) {
						var value = attributes[attr];
						if (attr == 'class')
							value = 'dsplus '+ value;
						input.setAttribute(attr, value);
					}
				}
				return input;
			}
			function createSelect(id, options, selectedIdx, attributes) {
				var select = _ce('select', '', {
					'id': id,
					'class': 'dsplus'
				});
				for (var i = 0, max = options.length; i < max; i++) {
					var option = _ce('option', options[i]);
					select.appendChild(option);
				}
				if (selectedIdx)
					select.selectedIndex = selectedIdx;
				if (attributes) {
					for (var attr in attributes) {
						var value = attributes[attr];
						if (attr == 'class')
							value = 'dsplus '+ value;
						select.setAttribute(attr, value);
					}
				}
				return select;
			}
			var settingsStructure = {
				tabOrder: ['general', 'farming', 'map', 'reports', 'paFeatures', 'hotkeys', 'misc'],
				general: {
					group0: {
						type: 'div',
						items: ['consider_successratio', 'sitting_enabled']
					},
					group_storageColor: {
						type: 'div',
						items: ['storageColor_enabledAt', 'storageColor_bar']
					}
				},
				farming: {
					group0: {
						type: 'table',
						items: ['farmlist_option', 'farmlist_attackedFarms', 'farming_farmSpys', 'farming_spyOnlyWithUnits', 'farming_sendRams']
					},
					group_farming_filter: {
						type: 'table',
						items: ['farming_filter_text', 'farming_minRes', 'farming_maxDistance', 'farming_maxAge', 'farming_maxRuntime']
					},
					group_bot: {
						type: 'div',
						caption: 'bot_enabled',
						items: ['bot_enabled', 'bot_farmOver']
					},
					group_units: {
						type: 'div',
						items: ['place_showUnitSelection', 'farming_calcUnitNumbers', 'farming_unitPreferences']
					},
					group_catas: {
						type: 'div',
						caption: 'farming_sendCatas',
						items: ['farming_sendCatas', 'farming_buildingPreferences']
					}
				},
				map: {
					group0: {
						type: 'div',
						items: ['map_overlays_enabled']
					}
				},
				reports: {
					group0: {
						type: 'table',
						items: ['reports_searchInput', 'reports_changeLinkStyle']
					},
					group1: {
						type: 'table',
						items: ['reports_dot_text', 'reports_newReport', 'reports_readReport', 'reports_spyReport', 'reports_oldReport']
					},
					group2: {
						type: 'div',
						items: ['reports_reorder', 'reports_showSurvivor', 'reports_showBashPoints']
					}
				},
				paFeatures: {
					group0: {
						type: 'div',
						caption: 'freePA',
						items: ['freePA', 'fileSite', 'showStats', 'statSite']
					},
					group1: {
						type: 'div',
						items: ['quickbar']
					},
					group2: {
						type: 'div',
						items: ['fa_text', 'fa_additions_enabled', 'fa_skipAttackedVillages', 'fa_filter_minRes', 'fa_filter_maxDistance']
					}
				},
				hotkeys: {
					group0: {
						type: 'div',
						caption: 'hotkeys_enabled',
						items: ['hotkeys_enabled', 'hotkeys_display', 'hotkeys_coordSelector']
					},
					group1: {
						type: 'div',
						items: ['userKeys']
					}
				},
				sounds: {
					group0: {
						type: 'div',
						items: ['sounds_captcha']
					}
				},
				misc: {
					group0: {
						type: 'div',
						items: ['template_units_enabled', 'template_buildings_enabled', 'queue_option', 'production_recruitment', 'production_removeCols']
					},
					group1: {
						type: 'div',
						items: ['production_filter_enabled', 'overview_showBooty', 'confirm_deleting', 'useEnhancedAnimations']
					},
					group2: {
						type: 'div',
						items: ['fixQuickbar', 'fixInfobar']
					}
				}
			};
			function createTab(tabKey, showAdvanced) {
				var tab = _ce('div', '', {
					'class': 'dsplus_tab',
					'id': 'dsplus_'+ tabKey +'_tab'
				});
				for (var groupKey in settingsStructure[tabKey]) {
					var group = settingsStructure[tabKey][groupKey];
					var groupContainer = _ce(group.type, '', {
						'class': 'content-border advanced'
					});
					tab.appendChild(groupContainer);
					var isTable = (group.type == 'table');
					for (var i = 0, key; key = group.items[i]; i++) {
						Settings.optionsToCheck.push(key);

						var optionContainer, optionRow;
						if (isTable) {
							optionRow = groupContainer.insertRow(-1);
							optionContainer = optionRow.insertCell(-1);
							optionContainer.colSpan = 2;
						} else {
							optionContainer = _ce('div', '', {
								'class': 'dsplus_option_container'
							});
							groupContainer.appendChild(optionContainer);
						}
						if (group.caption == key)
							optionContainer.classList.add('caption');

						var option = Settings.options[key];
						var text = texts.settings[key];
						var classList = (isTable ? optionRow : optionContainer).classList;
						if (option.advanced) {
							classList.add('advanced');
						} else {
							groupContainer.classList.remove('advanced');
						}

						if (option.type == 0) {
							if (isTable) {
								optionContainer = optionRow.insertCell(0);
								optionContainer.classList.add('caption');
							}
							optionContainer.classList.remove('show');
							optionContainer.style.fontWeight = 'bold';
							optionContainer.innerHTML = text +':';
						} else if (option.type == 1) {
							var checkbox = createCheckbox('dsplus_'+ key, Data.settings.get(key));
							optionContainer.appendChild(checkbox);
							var textSpan = _ce('span', text);
							textSpan.setAttribute('rel', 'dsplus_'+ key);
							optionContainer.appendChild(textSpan);
						} else if (option.type == 2) {
							var value = Data.settings.get(key) || [true, 500];
							var val = (is_array(value) ? value[1] : value);
							var preText = text;
							var sufText = '';
							if (texts.settings.hasOwnProperty(key +'_after'))
								sufText = texts.settings[key +'_after'];
							if (option.addFilterHint) {
								preText += ' (<i><u>'+ texts.settings.filterHint +'</u></i>)';
							}
							createCaption(optionContainer, preText, isTable);

							var enabled = true;
							if (option.enableCheckbox) {
								enabled = value[0];
								var enableCheckbox = createCheckbox('dsplus_'+ key +'_enabled', enabled);
								enableCheckbox.classList.add('settingEnable');
								enableCheckbox.title = texts.settings.enable;
								optionContainer.appendChild(enableCheckbox);
							}

							var input = createInputEl('dsplus_'+ key, val);
							optionContainer.appendChild(input);
							optionContainer.appendChild(_ce('span', sufText));

							if (option.showStdValue) {
								var stdVal = Data.settings.getStd(key)[1];
								var stdEl = _ce('span', '', {
									'class': 'grey',
									'style': 'font-size: 90%; margin-left: 10px;'
								});
								stdEl.innerHTML = '(<a class="insertStandard" data-value="'+ stdVal +'" href="javascript:;">'+ texts.settings.standard +': '+ stdVal +'</a>)';
								optionContainer.appendChild(stdEl);
							}
						} else if (option.type == 3) {
							createCaption(optionContainer, text, isTable);

							var optionTexts = texts.settings[key +'_options'];
							var select = createSelect('dsplus_'+ key, optionTexts, Data.settings.get(key));
							optionContainer.appendChild(select);
						} else if (option.type == 4) {
							if (Settings.hasOwnProperty('make_'+ key)) {
								eval('Settings.make_'+ key +'(text, optionContainer)');
							} else {
								_remove(optionContainer);
							}
						}
					}
				}
				return tab;

				function createCaption(optionContainer, text, isTable) {
					if (isTable) {
						optionContainer.colSpan = 1;
						var captionCell = optionContainer.parentNode.insertCell(0);
						captionCell.classList.add('caption');
						captionCell.innerHTML = text +':';
					} else {
						optionContainer.appendChild(_ce('span', text +': '));
					}
				}
			}
			function insertIcon() {
				// integrate in the menu
				var newCell = _ce('td');

				var base64Icon = '';
				var settings_link = _ce('a', '&nbsp;', {
					'id': 'dsplus_settings_link',
					'href': 'javascript:;',
					'class': 'manager_icon',
					'style': 'background-image:url(\''+ base64Icon +'\');',
					'title': texts.gui.title +' '+ texts.settings.settings
				});
				settings_link.addEventListener('click', function() {
					tabs_init();

					window.scrollTo(0, 0);
				}, true);
				newCell.appendChild(settings_link);

				newCell.addEventListener('mouseenter', function() {
					Bot.pause();
				}, false);
				newCell.addEventListener('mouseleave', function() {
					Bot.resume();
				}, false);

				var tds = document.querySelectorAll('td.menu-item');
				_after(newCell, tds[6]);
			}
			function tabs_init() {
				Bot.pause();
				Settings.optionsToCheck = [];

				// set width
				var popupWidth = 760;
				// calc height
				var top = __('topContainer').offsetHeight + 4;
				var bottom = __('footer').offsetHeight;
				var contentHeight = window.innerHeight - top - bottom;
				var popupHeight = contentHeight - 24;
				popupHeight = Math.max(Math.min(popupHeight, 820), 350);
				// calc top
				var popupTop = (contentHeight - popupHeight) / 2 + top+8;
				popupTop = Math.max(popupTop, top+8);
				// add styles!
				Style.settings(popupWidth, popupHeight, popupTop);

				var showAdvanced = Data.settings.get('settings_advanced');
				// opened before?
				var settings_popup = __('dsplus_settings');
				if (settings_popup) {
					__('dsplus_settings_close').click();
					return;
				}
				settings_popup = _ce('div', '', {
					'id': 'dsplus_settings'
				});

				var navList = '';
				var tabFragment = doc.createDocumentFragment();
				var activeTab = Data.settings.get('settings_tab');
				for (var i = 0, tabKey; tabKey = settingsStructure.tabOrder[i]; i++) {
					var active = (activeTab == tabKey);
					var tabName = texts.settings.tabs[tabKey];
					navList += '<li><a href="javascript:;" id="dsplus_'+ tabKey +'" class="tabLink'+ (active ? ' selected' : '') +'">'+ tabName +'</a></li>';
					if (active) {
						tabFragment.appendChild(createTab(tabKey, showAdvanced));
					}
				}

				var settings_menu = _ce('div', texts.gui.title +' ', {
					'class': 'menu_bar popup_menu',
					'style': 'font-size: 17px; font-weight: bold; cursor: move;'
				});
				var infoImg = _ce('img', '', {
					'class': 'tooltip',
					'src': image_base +'overview/note.png',
					'title': texts.gui.title +': '+ lib.getScriptVersion() +'<br>' +
						texts.gui.twVersion +': '+ lib.getForTWVersion() +' (build '+ lib.getForTWBuild() +')<br>' +
						texts.gui.dataVersion +': '+ lib.getDataVersion()
				});
				infoImg.addEventListener('click', function(e) {
					e.preventDefault();
					e.stopPropagation();
					window.open('http://www.fileface.de/index.php?title=Changelog#'+ lib.getScriptVersion());
				}, true);
				settings_menu.appendChild(infoImg);
				var closeLink = _ce('a', 'X', {
					'id': 'dsplus_settings_close',
					'href': 'javascript:;'
				});
				closeLink.addEventListener('click', function() {
					settings_popup.classList.remove('open');

					Bot.resume();
					setTimeout(function() {
						_remove(settings_popup);
					}, 400);
				}, true);
				settings_menu.appendChild(closeLink);
				settings_popup.appendChild(settings_menu);

				// Tabs
				var settings_nav = _ce('ul', navList, {
					'id': 'dsplus_settings_nav',
					'class': 'widget-tabs'
				});
				var advancedContainer = _ce('div', '', {
					'class': 'float_right',
					'style': 'padding: 4px; font-weight: bold;'
				});
				var advancedCheckbox = _ce('input', '', {
					'id': 'dsplusSettingsAdvanced',
					'type': 'checkbox'
				});
				advancedCheckbox.checked = showAdvanced;
				var advancedSpan = _ce('span', texts.settings.advanced, {
					'id': 'dsplusSettingsAdvancedSpan'
				});
				advancedContainer.appendChild(advancedCheckbox);
				advancedContainer.appendChild(advancedSpan);
				settings_nav.appendChild(advancedContainer);
				settings_popup.appendChild(settings_nav);

				var settings_main = _ce('div', '', {
					'id': 'dsplus_settings_main',
					'style': 'height: '+ (popupHeight-73) +'px;'
				});
				if (showAdvanced)
					settings_main.classList.add('advanced');
				settings_popup.appendChild(settings_main);

				// info popup (export, import & reset)
				var info_popup = _ce('div', '', {
					'id': 'dsplus_settings_info_popup',
					'style': 'bottom: -500px;'
				});
				var info_popup_menu = _ce('div', '', {
					'class': 'menu_bar'
				});
				var info_popup_title = _ce('span');
				info_popup_menu.appendChild(info_popup_title);
				var info_popup_close = _ce('a', texts.gui.X, {
					'class': 'close float_right',
					'href': 'javascript:;',
				});
				info_popup_close.addEventListener('click', closeInfoPopup, false);
				info_popup_menu.appendChild(info_popup_close);
				info_popup.appendChild(info_popup_menu);
				settings_popup.appendChild(info_popup);

				// footer (containing save&close, import, export, ...)
				var base64Icon_translationIcon = '';
				var settings_footer = _ce(
					'div',
					'<input type="button" class="btn float_left" id="dsplusSettingsSave" value="'+ texts.settings.save + ' & '+ texts.gui.close +'">' +
					'<input type="button" class="btn float_left" id="dsplusExport" value="'+ texts.settings.export.button +'">' +
					'<input type="button" class="btn float_left" id="dsplusImport" value="'+ texts.settings.import.button +'">' +
					'<input type="button" class="btn float_left" id="dsplusReset" value="'+ texts.settings.reset.button +'">' +
					'<a href="http://www.fileface.de/" target="_blank" class="float_left" style="margin: 2px 10px;">'+ lib.guillemetRight +' Wiki</a>' +
					'<a href="https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=FTRBXRLMYW5JL" target="_blank" class="float_left" style="margin-left: 6px; height: 21px;"><img src="https://www.paypalobjects.com/'+ texts.lang +'/i/btn/btn_donate_SM.gif" alt="Donate" title=""></a>' +
					'<button id="dsplusUpdateTranslation" class="btn float_right" style="padding: 0px;" title="Update Translation"><img src="'+ base64Icon_translationIcon +'"></button>',
					{
						'id': 'dsplus_settings_footer',
						'class': 'menu_bar'
					}
				);
				settings_popup.appendChild(settings_footer);

				settings_main.appendChild(tabFragment);

				doc.body.appendChild(settings_popup);


				// fix height:
				var newHeight = popupHeight - settings_menu.offsetHeight - settings_nav.offsetHeight - settings_footer.offsetHeight;
				settings_main.style.height = newHeight +'px';
				lib.executeFnInUnsafewindow(function() {
					UI.Draggable(__('dsplus_settings'));
				});

				settings_popup.classList.add('open');
				if (!Data.settings.get('useEnhancedAnimations')) {
					settings_popup.classList.add('immediately');
				}

				// Alle Events etc. binden:
				if (Settings.hasOwnProperty('attach_'+ activeTab))
					eval('Settings.attach_'+ activeTab +'()');

				settings_popup.addEventListener('click', clickSettingsPopup, false);

				var inputs = _T(settings_main, 'input');
				for (var i = 0, input; input = inputs[i]; i++) {
					if (input.type != 'text')
						continue;
					if (_T(input.parentNode, 'input').length > 1) {
						input.addEventListener('change', function() {
							var key = this.id.match(/dsplus_(.+)/)[1];
							var value = Settings.parseValue(key, this.value);
							if (value != this.value)
								this.value = value;
						});
					}
				}

				window.setTimeout(function() {
					recreateToolTip();
				}, 500);

				var openInfoBtn = null;
				function setInfoPopupBottom() {
					info_popup.style.bottom = '-'+ (info_popup.offsetHeight-25) +'px';
				}
				function closeInfoPopup() {
					setInfoPopupBottom();
					if (openInfoBtn)
						openInfoBtn.classList.remove('btn-green');
					openInfoBtn = null;
				}
				function isOpenInfoPopup() {
					return !info_popup.style.bottom;
				}
				function openInfoPopup(btn, type, lastRowsHTML) {
					info_popup_title.innerHTML = texts.settings[type].title;

					if (openInfoBtn) {
						openInfoBtn.classList.remove('btn-green');
					}
					openInfoBtn = btn;
					openInfoBtn.classList.add('btn-green');

					var id = 'dsplus_'+ type;
					var isImport = type == 'import';
					var el = __(id);
					// already open?
					if (el && el.style.display != 'none' && isOpenInfoPopup()) {
						return true;
					}
					if (!el) {
						var HTML = '';
						var disabled = isImport ? ' disabled' : '';
						for (var key in texts.settings.port) {
							if (key == 'all')
								continue;

							HTML += '<tr><td><input type="checkbox" id="dsplus_'+ type +'_'+ key +'"'+ disabled +'></td><td>'+ texts.settings.port[key] +'</td></tr>';
						}
						if (!isImport) {
							HTML += '<tr><th colspan="2" style="padding-top: 2px;"></th></tr>' +
								'<tr><td><input type="checkbox" id="dsplus_'+ type +'_all"></td><td><i>'+ texts.settings.port.all +'</i></td></tr>' +
								'<tr><td colspan="2" style="padding-top: 8px;"></td></tr>';
						}
						HTML += lastRowsHTML;
						var el = _ce('table', HTML, {
							'id': id,
							'style': 'margin: 5px 10px;'
						});
						info_popup.appendChild(el);
					}
					for (var i = 1, child; child = info_popup.children[i]; i++) {
						if (child.id == id) _show(child);
						else _hide(child);
					}
					info_popup.style.left = btn.offsetLeft +'px';
					if (info_popup.style.bottom) {
						info_popup.style.transition = 'all 0s';
						setInfoPopupBottom();
						window.setTimeout(function() {
							info_popup.style.transition = '';
							info_popup.style.bottom = '';
						});
						if (isImport && Data.settings.get('useEnhancedAnimations')) {
							setTimeout(function() {
								__('dsplus_import_code').select();
							}, 500);
						} else if (isImport) {
							__('dsplus_import_code').select();
						}
					} else if (isImport) {
						__('dsplus_import_code').select();
					}
					if (!isImport) {
						var elIDs = ['dsplus_'+ type +'_reports', 'dsplus_'+ type +'_villages', 'dsplus_'+ type +'_coords'];
						for (var i = 0, ID; ID = elIDs[i]; i++) {
							__(ID).addEventListener('click', function() {
								var check = this.checked;
								for (var i = 0, ID; ID = elIDs[i]; i++) {
									if (this.id != ID)
										__(ID).checked = check;
								}
							}, false);
						}
						__('dsplus_'+ type +'_all').addEventListener('click', function() {
							var check = this.checked;
							var tBody = this.parentNode.parentNode.parentNode;
							var inputs = _T(tBody, 'input');
							for (var i = 0, input; input = inputs[i]; i++) {
								if (input.type == 'checkbox')
									input.checked = check;
							}
						}, false);
					}
					return false;
				}

				function clickSettingsPopup(e) {
					var target = e.target;
					var par = target.parentNode;
					if (target.id == 'dsplusSettingsSave') {
						/*
						 * Save everything
						 */
						Settings.saveClose();
						return;
					} else if (target.id == 'dsplusExport') {
						/*
						 * Export data
						 */
						var alreadyOpen = openInfoPopup(target, 'export',
							'<tr>' +
								'<td><label for="dsplus_export_code">'+ texts.settings.export.code +':</label></td>' +
								'<td>'+ GUI.settings.createInput('dsplus_export_code', '', '', 'readonly="readonly"') +'</td>' +
							'</tr>'
						);

						if (alreadyOpen) {
							Settings.doExport();
						}
						return;
					} else if (target.id == 'dsplusImport') {
						/*
						 * Import data
						 */
						var alreadyOpen = openInfoPopup(target, 'import',
							'<tr><th colspan="2" style="padding-top: 2px;"></th></tr>' +
							'<tr>' +
								'<td><label for="dsplus_import_code">'+ texts.settings.import.code +':</label>&nbsp;</td>' +
								'<td>'+ GUI.settings.createInput('dsplus_import_code', '') +'</td>' +
							'</tr>'
						);

						if (alreadyOpen) {
							Settings.doImport();
						}
						return;
					} else if (target.id == 'dsplusReset') {
						/*
						 * Reset data
						 */
						var alreadyOpen = openInfoPopup(target, 'reset', '');

						if (alreadyOpen && Settings.doReset()) {
							closeInfoPopup();
						}
						return;
					} else if (target.id == 'dsplusUpdateTranslation') {
						lib.updateTexts();
						return;
					}
					if (isOpenInfoPopup()) {
						var tmp = target;
						while (tmp.id != 'dsplus_settings_info_popup' && tmp.id != 'dsplus_settings') {
							tmp = tmp.parentNode;
						}
						if (tmp.id == 'dsplus_settings') {
							closeInfoPopup();
						}
					}
					if (target.id == 'dsplusSettingsAdvanced' || target.id == 'dsplusSettingsAdvancedSpan') {
						var advancedCheckbox = target;
						if (target.id == 'dsplusSettingsAdvancedSpan') {
							advancedCheckbox = _prev(target);
							advancedCheckbox.checked = !advancedCheckbox.checked;
						}
						showAdvanced = advancedCheckbox.checked;
						Data.settings.set('settings_advanced', showAdvanced);
						Data.settings.save();

						settings_main.classList.remove('advanced');
						if (showAdvanced)
							settings_main.classList.add('advanced');
					} else if (target.classList.contains('insertStandard')) {
						var cell = target.parentNode.parentNode;
						var relatedInput = cell.querySelector('input[type="text"]');
						if (relatedInput) {
							relatedInput.value = target.dataset.value;
						}
					} else if (target.classList.contains('tabLink') || par.classList.contains('tabLink')) {
						if (par.classList.contains('tabLink')) {
							target = par;
						}
						target.blur();

						// make the tab link selected
						var selectedTabLink = doc.querySelector('a.tabLink.selected');
						if (selectedTabLink) {
							selectedTabLink.classList.remove('selected');
						}
						target.classList.add('selected');

						// show the right tab or create it if necessary
						var tableID = target.id +'_tab';
						var tabTables = settings_main.children;
						var insertTab = true;
						for (var i = 0, table; table = tabTables[i]; i++) {
							if (table.id == tableID) {
								_show(table);
								insertTab = false;
							} else {
								_hide(table);
							}
						}

						// create the tab:
						var tabName = target.id.replace('dsplus_', '');
						if (insertTab) {
							var newTab = createTab(tabName, showAdvanced);
							_show(newTab);
							settings_main.appendChild(newTab);

							if (Settings.hasOwnProperty('attach_'+ tabName))
								eval('Settings.attach_'+ tabName +'()');
						}

						Data.settings.set('settings_tab', tabName);
						Data.settings.save();
					} else if (target.hasAttribute('rel') || par.hasAttribute('rel')) {
						var relSpan = target;
						if (!target.hasAttribute('rel')) relSpan = par;
						var relId = relSpan.getAttribute('rel');
						var checkbox = __(relId);
						if (checkbox)
							checkbox.checked = !checkbox.checked;
					}
				}
			}

			// settings
			return {
				colorInput: colorInput,
				createCheckbox: createCheckbox,
				createInput: createInput,
				createInputEl: createInputEl,
				createSelect: createSelect,
				insertIcon: insertIcon
			};
		})();

		filter = (function () {
			function getFilterOptionsDiv() {
				var filterOptionsDiv = _ce('div', '', {
					'class': 'vis_item',
					'style': 'margin: 0; padding: 0; background: transparent;'
				});
				if (!Data.get('filter_open')) {
					filterOptionsDiv.style.display = 'none';
				}

				var table = _ce('table', '', {
					'class': 'vis',
					'style': 'width: 100%;'
				});

				for (var i = 0, filterOption; filterOption = Filter.filter[i]; i++) {
					if (!Filter.isUsed(filterOption))
						continue;

					var row = table.insertRow(-1);
					row.id = 'dsplus_filterRow_'+ filterOption;

					var cell = row.insertCell(-1);
					cell.innerHTML = texts.gui['filter_'+ filterOption].replace('{char}', Filter.isInverted(filterOption) ? '&le;' : '&gt;') +':';
					var filterEnable = _ce('input', '', {
						'type': 'checkbox',
						'id': 'dsplus_filterEnable_'+ filterOption
					});
					filterEnable.checked = Filter.isEnabled(filterOption);
					filterEnable.addEventListener('change', function() {
						var optionKey = this.id.replace('dsplus_filterEnable_', '');
						Filter.setEnabled(optionKey, this.checked);
						classifyFilterLink();
					}, false);
					_prepend(filterEnable, cell);

					var cell = row.insertCell(-1);
					var filterValue = _ce('input', '', {
						'type': 'text',
						'id': 'dsplus_filterValue_'+ filterOption,
						'value': Filter.getValue(filterOption)
					});
					filterValue.addEventListener('keyup', function() {
						var optionKey = this.id.replace('dsplus_filterValue_', '');
						Filter.setValue(optionKey, this.value);
					}, false);
					cell.appendChild(filterValue);
					var filterInvert = _ce('input', '', {
						'type': 'checkbox',
						'id': 'dsplus_filterInvert_'+ filterOption,
						'style': 'margin-left: 1.8em;'
					});
					filterInvert.checked = Filter.isInverted(filterOption);
					filterInvert.addEventListener('change', function() {
						var optionKey = this.id.replace('dsplus_filterInvert_', '');
						Filter.setInverted(optionKey, this.checked);

						var charSpan = _prev(this.parentNode).querySelector('span.char');
						if (charSpan)
							charSpan.innerHTML = Filter.isInverted(optionKey) ? '&le;' : '&gt;';
					}, false);
					cell.appendChild(filterInvert);
					cell.appendChild(_ce('span', texts.gui.filterInvert));
				}

				filterOptionsDiv.appendChild(table);

				return filterOptionsDiv;
			}
			function classifyFilterLink() {
				var filterLink = __('dsplus_filterLink');
				var img = filterLink.children[0];
				var activeFilter = Filter.isFilterActive();
				if (activeFilter) {
					img.style.display = 'inline';
				} else {
					_hide(img);
				}
			}

			function updateMatchCounts() {
				for (var i = 0, filterOption; filterOption = Filter.filter[i]; i++) {
					var filterRow = __('dsplus_filterRow_'+ filterOption);
					if (filterRow) {
						var newTitle = '';
						if (Filter.isUsed(filterOption) && Filter.isEnabled(filterOption))
							newTitle = texts.gui.filterAppliesTo.replace('%count%', Filter.matchCount[filterOption]);
						filterRow.title = newTitle;
					}
				}

				var filterLink = __('dsplus_filterLink');
				var img = filterLink.children[0];
				img.title = texts.gui.filterAppliesTo.replace('%count%', Filter.matchCount.total);
			}

			function initOverview() {
				var table = __('combined_table') || __('production_table') || __('buildings_table') || __('techs_table') || __('group_assign_table');
				if (!table)
					return;

				var type = table.id.replace('_table', '');
				Filter.observerList.push(function(initialize) {
					if (!initialize)
						updateMatchCounts();
				});
				Filter.initOverview(type);

				Style.filter();
				var filterDiv = _ce('div', '', {
					'class': 'vis_item',
					'style': 'clear: both;'
				});
				var filterLink = _ce('a', lib.guillemetRight +' '+ texts.gui.filter +' <img src="'+ image_base +'stat/green.png" alt="active" style="display: none;">', {
					'id': 'dsplus_filterLink',
					'href': 'javascript:;'
				});
				filterDiv.appendChild(filterLink);
				_before(filterDiv, table);
				classifyFilterLink();

				var filterOptionsDiv = getFilterOptionsDiv();
				_before(filterOptionsDiv, table);

				filterLink.addEventListener('click', function(e) {
					// if it is now hidden -> the new Status is visible
					var openStatus = !(filterOptionsDiv.style.display != 'none');
					Filter.setOpen(openStatus);

					jQuery(filterOptionsDiv).slideToggle(80);
				}, true);

				updateMatchCounts();
			}

			function initReports() {
				var firstTH = _T(__('report_list'), 'th', 0);
				var search_text = Data.get('search_text');
				var oldHTML = firstTH.innerHTML;
				var val = search_text || oldHTML;
				firstTH.innerHTML = '<div style="position: relative;">' +
					'<input type="text" id="dsplus_search" value="'+ val +'">' +
					'<a id="dsplus_search_delete" style="display: none;" href="javascript:;"><img alt="X" src="'+ image_base +'delete.png"></a>' +
				'</div>';
				firstTH.style.padding = 0;
				// delay setting fixed width
				setTimeout(function() {
					firstTH.style.width = firstTH.offsetWidth +'px';
				});

				var dsplus_search = __('dsplus_search');
				var deleteSearchText = __('dsplus_search_delete');
				dsplus_search.addEventListener('focus', function() {
					if (this.value == oldHTML)
						this.value = '';
				}, false);
				dsplus_search.addEventListener('blur', function() {
					if (!this.value)
						this.value = oldHTML;
				}, false);
				var oldChecked = {};
				var search = function(e) {
					e = e || window.event || {};
					var keyCode = e.keyCode ? e.keyCode : (e.charCode ? e.charCode : e.which);
					// escape
					if (keyCode == 27) {
						dsplus_search.value = '';
					}
					var search_str = dsplus_search.value;
					Data.set('search_text', search_str);
					Data.save();
					var firstTH = _T(__('report_list'), 'th', 0);
					var row = _next(firstTH.parentNode);
					while (row) {
						var tds = _T(row, 'td');
						if (tds.length) {
							var link = _T(row, 'a', 0);
							if (link) {
								var checkbox = _T(row, 'input', 0);
								if (link.textContent.trim().indexOf(search_str) < 0) {
									if (checkbox.checked) {
										oldChecked[checkbox.name] = true;
										checkbox.checked = false;
									}
									_hide(row);
								} else {
									if (oldChecked[checkbox.name]) {
										checkbox.checked = true;
										delete oldChecked[checkbox.name];
									}
									_show(row);
								}
							}
						}
						row = _next(row);
					}
					if (keyCode == 27) {
						dsplus_search.blur();
					}
					if (search_str) _show(deleteSearchText);
					else _hide(deleteSearchText);
				};
				if (search_text) search();
				dsplus_search.addEventListener('keyup', search, false);

				deleteSearchText.addEventListener('click', function() {
					dsplus_search.value = '';
					search();
					dsplus_search.value = oldHTML;
				}, true);

				var select_all = __('select_all');
				if (select_all) {
					select_all.setAttribute('onclick', '');
					select_all.addEventListener('click', function() {
						var checkboxes = __('report_list').querySelectorAll('input[type="checkbox"]');
						var check = select_all.checked;
						for (var i = 0, checkbox; checkbox = checkboxes[i]; i++) {
							var row = checkbox.parentNode.parentNode;
							if (row.style.display != 'none')
								checkbox.checked = check;
						}
					}, false);
				}
			}

			// filter
			return {
				initOverview: initOverview,
				initReports: initReports
			};
		})();

		reports = (function () {
			var reorderContainer;
			var containers = {};
			function initReorder(reportCell, conts) {
				reorderContainer = __('reorderContainer');
				containers = conts;

				Style.add('reorder').set(
					'#dsplus_reportOrder_container { text-align: right; position: relative; font-size: 0.8em; font-weight: normal; }' +
					'#dsplus_reportOrder { position: absolute; width: 250px; height: 100px; display: none; top: -10px; }' +
					'#dsplus_reorder_tab { text-align: left; border: 1px solid black; border-left: 0px; box-shadow: 0px 0px 4px; }' +
					'#dsplus_reorder_tab tr.elRow:hover { box-shadow: 0 0 2px blue; }' +
					'.reorder_hover { box-shadow: 0 0 2px blue; }'
				);

				var headline = reportCell.children[0];
				var reorderDiv = _ce('div', '', {
					'id': 'dsplus_reportOrder_container',
					'class': 'float_right align_right'
				});
				headline.appendChild(reorderDiv);

				var reorderLink = _ce('a', texts.gui.reportOrder.title, {
					'id': 'dsplus_reportOrder_link',
					'href': 'javascript:;'
				});
				reorderLink.addEventListener('click', function() {
					writeReorderPopup();
					var $parent = jQuery(this).parent();
					var $reportCell = jQuery(reportCell);
					var left = $reportCell.offset().left + $reportCell[0].offsetWidth - $parent.offset().left;
					var reorderPopup = __('dsplus_reportOrder');
					reorderPopup.style.left = Math.round(left) +'px';
					jQuery(reorderPopup).fadeIn(400);
				}, true);
				reorderDiv.appendChild(reorderLink);

				var tabHTML = '<table id="dsplus_reorder_tab" class="vis popup_content">' +
					'<tbody id="dsplus_reorder"></tbody>' +
					'<tfoot><tr><td style="background-color: transparent;" colspan="2"><button id="dsplus_submit_order">'+ texts.gui.OK +'</button><button id="dsplus_cancel_order">'+ texts.gui.X +'</button></td></tr></tfoot>' +
				'</table>';
				var reorderPopup = _ce('div', tabHTML, {
					'id': 'dsplus_reportOrder'
				});
				reorderDiv.appendChild(reorderPopup);
				__('dsplus_submit_order').addEventListener('click', function() {
					jQuery(reorderPopup).fadeOut(100);
					Data.set('report_els_order', Report.els_order);
					Data.set('report_els_enabled', Report.els_enabled);
					Data.save();
				}, true);
				__('dsplus_cancel_order').addEventListener('click', function() {
					Report.els_order = Data.get('report_els_order');
					Report.els_enabled = Data.get('report_els_enabled');
					jQuery(reorderPopup).fadeOut(100);
					reorder();
				}, true);

				reorder();
			}
			function reorder() {
				var afterEl = null;
				for (var i = 0, maxi = Report.els_order.length; i < maxi; i++) {
					var item = Report.els_order[i];
					var active = Report.els_enabled[item];
					var cont = containers[item];
					if (!cont)
						continue;

					reorderContainer.appendChild(cont);
					if (active) _show(cont);
					else _hide(cont);
				}
			}
			function writeReorderPopup() {
				var HTML = '';
				for (var i = 0, el; el = Report.els_order[i]; i++) {
					HTML += '<tr data-el-key="'+ el +'" class="elRow">' +
						'<td style="background-color: transparent;"><input name="dsplus_reorder_'+ el +'" type="checkbox"'+ (Report.els_enabled[el] ? ' checked' : '') +'></td>' +
						'<td>'+ texts.gui.reportOrder[el] +'</td>' +
						'<td style="background-color: transparent;"><div class="bqhandle"> </div></td>' +
					'</tr>';
				}
				var reorderEl = __('dsplus_reorder');
				reorderEl.innerHTML = HTML;
				reorderEl.addEventListener('mouseover', function(e) {
					var target = e.target;
					var targetRow = target.parentNode;
					if (targetRow.tagName != 'TR' || !targetRow.classList.contains('elRow'))
						return;

					var elKey = targetRow.dataset.elKey;
					var container = Report.containers[elKey];
					if (container) container.classList.add('reorder_hover');
				}, false);
				reorderEl.addEventListener('mouseout', function(e) {
					var target = e.target;
					var targetRow = target.parentNode;
					if (targetRow.tagName != 'TR' || !targetRow.classList.contains('elRow'))
						return;

					var elKey = targetRow.dataset.elKey;
					var container = Report.containers[elKey];
					if (container) container.classList.remove('reorder_hover');
				}, false);

				jQuery(reorderEl).sortable({
					axis: 'y',
					handle: '.bqhandle',
					update: function() {
						var order = [];
						var rows = reorderEl.rows;
						for (var i = 0, row; row = rows[i]; i++) {
							var input = _T(row, 'input', 0);
							if (input) {
								var name = input.name.replace('dsplus_reorder_', '');
								order.push(name);
							}
						}
						Report.els_order = order;
						reorder();
					}
				});
				var inputs = _T(reorderEl, 'input');
				for (var i = 0, input; input = inputs[i]; i++) {
					input.addEventListener('click', function() {
						var name = this.name && this.name.replace('dsplus_reorder_', '');
						if (!name) return;
						Report.els_enabled[name] = this.checked;
						reorder();
					}, false);
				}
			}

			// reports
			return {
				initReorder: initReorder,
				writeReorderPopup: writeReorderPopup
			};
		})();

		map = (function () {
			function init() {
				// add styles
				Style.add('map').set(
					''
				);
			}

			// map
			return {
				init: init
			};
		})();


		return {
			loading: loading,
			hideScriptWarnings: hideScriptWarnings,
			fixQuickbar: fixQuickbar,
			fixInfobar: fixInfobar,
			addPlayerLink: addPlayerLink,
			initErrorGUI: initErrorGUI,
			settings: settings,
			filter: filter,
			reports: reports,
			map: map
		};
	})(jQuery);


	var Color = function() {
		this.r = 0;
		this.g = 0;
		this.b = 0;
		this.a;
		this.fromString = function(str) {
			str = str.replace(/\s*/g, '');
			var res = str.match(/^rgb[a]?\(\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d+)\s*(,\s*(\d+)\s*)?\)$/i);
			if (res) {
				this.r = res[1];
				this.g = res[2];
				this.b = res[3];
				if (res[4]) {
					this.a = res[5];
				}
			} else {
				res = str.match(/^#?([0-9a-f]{1,2})([0-9a-f]{1,2})([0-9a-f]{1,2})$/i);
				this.r = parseInt(res[1], 16);
				this.g = parseInt(res[2], 16);
				this.b = parseInt(res[3], 16);
			}
			return this;
		};
		this.toString = function(rgb, a) {
			if (rgb) {
				var alpha = typeof(a) != 'undefined' || typeof(this.a) != 'undefined';
				if (alpha) {
					if (typeof(a) != 'undefined') a = this.a;
					return 'rgba('+ this.r +','+ this.g +','+ this.b +','+ a +')';
				} else
					return 'rgb('+ this.r +','+ this.g +','+ this.b +')';
			} else {
				return '#' + (0x100 | this.r).toString(16).substr(1) + (0x100 | this.g).toString(16).substr(1) + (0x100 | this.b).toString(16).substr(1);
			}
		};
		this.invert = function() {
			this.r = Math.abs(255-this.r);
			this.g = Math.abs(255-this.g);
			this.b = Math.abs(255-this.b);
			return this;
		};
		this.getContrastColor = function() {
			return 0.213*this.r + 0.715*this.g + 0.072*this.b < 128 ? new Color(255,255,255) : new Color(0,0,0);
		};
		this.fadeTo = function(p, color) {
			p = Math.min(Math.max(p, 0), 100);
			return new Color(
				Math.round(this.r + (color.r-this.r)*p),
				Math.round(this.g + (color.g-this.g)*p),
				Math.round(this.b + (color.b-this.b)*p)
			);
		};
		switch(arguments.length) {
		case 0:
			break;
		case 1:
			this.fromString(arguments[0]);
			break;
		case 3:
			this.r = arguments[0];
			this.g = arguments[1];
			this.b = arguments[2];
			break;
		default:
			throw new TypeError('Color: invalid parameters');
		}
	};


	function ColorBar(colorBarID, container, cols, std) {
		var colors = clone(cols);
		var gradiantColors = [];

		Style.add('colorBar').set(
			'.colorBar { cursor: pointer; height: 20px; width: 707px; border: 1px solid black; margin: 0 0 18px; }' +
			'.arrow { width: 7px; height: 20px; position: relative; top: 18px; cursor: default; }'
		);

		var colorBarElID = 'dsplus_'+ colorBarID +'ColorBar';

		var colorBarEl = _ce('div', '', {
			'class': 'colorBar'
		});
		colorBarEl.addEventListener('click', colorBarElClick, false);
		var colorBarArrow = _ce('div', '&#8593;', {
			'class': 'arrow',
			'style': 'left: -9999px;'	// hide the arrow at the beginning
		});
		colorBarEl.appendChild(colorBarArrow);
		container.appendChild(colorBarEl);

		/*
		 * control table
		 */
		var controlTable = _ce('table', '', {
			'style': 'width: 100%; text-align: center;'
		});
		var controlRow = controlTable.insertRow(-1);

		/*
		 * cell with percentage-, color-input and change-button
		 */
		var editCell = controlRow.insertCell(-1);
		editCell.style.width = '50%';
		var tab = _ce('table', '', {
			'style': 'margin: 0 auto;'
		});

		var row = tab.insertRow(-1);
		var cell = row.insertCell(-1);
		cell.style.textAlign = 'right';
		cell.appendChild(_text(texts.gui.percentage +':'));
		var cell = row.insertCell(-1);
		cell.style.textAlign = 'left';
		// percent-input
		var percentage = _ce('input', '', {
			'type': 'text',
			'size': 7,
			'style': 'border: 1px solid gray;'
		});
		percentage.addEventListener('keyup', percentageKeyUp, false);
		cell.appendChild(percentage);
		cell.appendChild(_text(' %'));

		var row = tab.insertRow(-1);
		var cell = row.insertCell(-1);
		cell.style.textAlign = 'right';
		cell.appendChild(_text(texts.gui.color +':'));
		var cell = row.insertCell(-1);
		cell.style.textAlign = 'left';
		// color box
		var colorInput = _ce('input', '', {
			'type': 'text',
			'size': 7,
			'style': 'border: 1px solid gray;'
		});
		colorInput.addEventListener('keyup', colorInputKeyUp, false);
		cell.appendChild(colorInput);

		var row = tab.insertRow(-1);
		row.insertCell(-1);
		var cell = row.insertCell(-1);
		cell.textAlign = 'left';
		var colorButton = _ce('input', '', {
			'type': 'button',
			'class': 'btn',
			'value': texts.gui.change
		});
		colorButton.addEventListener('click', colorButtonClick, true);
		cell.appendChild(colorButton);

		editCell.appendChild(tab);

		/*
		 * cell with the colors (for each color one row)
		 */
		var colorTableCell = controlRow.insertCell(-1);
		colorTableCell.style.width = '50%';
		var colorTable = _ce(
			'table',
			'<tr><th style="text-align: right;">%</th><th>'+ texts.gui.colorCode +'</th><th>&nbsp;</th></tr>',
			{
				'class': 'vis',
				'style': 'margin: 0 auto;'
			}
		);
		// click on a number-link
		colorTable.addEventListener('click', colorTableClick, false);
		colorTableCell.appendChild(colorTable);

		container.appendChild(controlTable);

		for (var key in colors) {
			addColorRow(parseInt(key, 10), colors[key]);
		}
		reloadColors();

		// color-areas


		/*
		 * event functions
		 */
		function percentageKeyUp() {
			if (/[^\d]/.test(percentage.value)) {
				var pos = getCursorPosition(percentage);
				var match = percentage.value.substr(0, pos).match(/[^\d]/g);
				var count = match && match.length || 0;
				percentage.value = percentage.value.replace(/[^\d]/g, '');
				setCursorPosition(percentage, pos-count);
			}
			if (!percentage.value) {
				percentage.value = 0;
				percentage.focus();
				percentage.select();
			}

			var p = parseInt(percentage.value, 10);
			if (p > 100) {
				p = 100;
				percentage.value = 100;
			}
			var color = gradiantColors[p];
			colorInput.value = color.toString().toUpperCase();
			colorInputKeyUp();
			colorBarArrow.style.left = (p*7) +'px';
		}
		function colorInputKeyUp() {
			if (/[a-z]/.test(colorInput.value)) {
				var pos = getCursorPosition(colorInput);
				colorInput.value = colorInput.value.toUpperCase();
				setCursorPosition(colorInput, pos);
			}
			var val = colorInput.value.replace(/\s*/g, '');
			var match_hex = val.match(/^#?[0-9a-f]{3,6}$/i);
			var match_rgb = val.match(/^rgb\(\d+,\d+,\d+\)$/i);
			var match_rgba = val.match(/^rgba\(\d+,\d+,\d+,\d+\)$/i);
			if (match_hex || match_rgb || match_rgba) {
				var col = new Color(val);
				colorInput.style.backgroundColor = col.toString();
				colorInput.style.color = col.getContrastColor().toString();
			} else {
				colorInput.style.backgroundColor = '';
				colorInput.style.color = '';
			}
		}
		function colorBarElClick(e) {
			var offsetX = jQuery(colorBarEl).offset().left;
			var p = Math.min(Math.floor((e.clientX - offsetX) / 7), 100);
			var target = e.target;
			if (target == colorBarEl) {
				var color = gradiantColors[p];
				colorInput.value = color.toString().toUpperCase();

				colorBarArrow.style.left = (p*7) +'px';

				percentage.value = p;
				percentageKeyUp();
			}
		}
		function colorButtonClick() {
			var val = colorInput.value.replace(/\s*/g, '');
			var p = parseInt(percentage.value, 10);
			var match_hex = /^#?[0-9a-f]{3,6}$/i.test(val);
			var match_rgb = /^rgb[a]?\(\s*\d+\s*,\s*\d+\s*,\s*\d+\s*(,\s*\d+\s*)\)$/i.test(val);
			if (match_hex || match_rgb) {
				var color = new Color(val);
				colors[p] = [color.r, color.g, color.b];
				addColorRow(p, colors[p]);
			} else {
				deleteColorRow(p);
			}
		}
		function colorTableClick(e) {
			var target = e.target;
			if (target.tagName == 'A' && target.classList.contains('dsplus_percentage')) {
				percentage.value = target.innerHTML;
				percentageKeyUp();
			}
		}

		/*
		 * ohter functions
		 */
		function reloadColors() {
			var diff = 1;
			var num1 = 0;
			var num2 = 0;
			var linearGradient = 'linear-gradient(to right';
			gradiantColors = [];
			for (var p = 0; p < 101; p++) {
				var color = new Color(
					colors[num1][0] + Math.round( (colors[num2][0] - colors[num1][0]) / diff * (p - num1) ),
					colors[num1][1] + Math.round( (colors[num2][1] - colors[num1][1]) / diff * (p - num1) ),
					colors[num1][2] + Math.round( (colors[num2][2] - colors[num1][2]) / diff * (p - num1) )
				);
				gradiantColors[p] = color;
				if (colors[p]) {
					var val = colors[p];
					linearGradient += ', '+ color.toString() +' '+ p +'%';

					num1 = p;
					for (var j = p+1; j < 101 && !colors[j]; j++) ;
					num2 = j;
					diff = num2-num1;
				}
			}
			linearGradient += ')';
			colorBarEl.style.backgroundImage = linearGradient;
		}
		function addColorRow(p, color) {
			var tmp;
			var colorBarRows = colorTable.rows;
			var insertAfter = colorBarRows[0];
			color = new Color('rgb('+ color.toString() +')');
			for (var i = 1, row; row = colorBarRows[i]; i++) {
				tmp = parseInt(_T(row.children[0], 'a', 0).innerHTML, 10);
				if (tmp == p) {
					row.children[1].innerHTML = color.toString().toUpperCase();
					row.children[1].style.backgroundColor = color.toString();
					insertAfter = null;
				} else if (tmp < p) {
					insertAfter = row;
				}
			}
			if (insertAfter) {
				var newRow = _ce('tr',
					'<td style="text-align: right;"><a href="javascript:;" class="dsplus_percentage">'+ p +'</a></td>' +
					'<td style="background-color: '+ color.toString() +'">'+ color.toString().toUpperCase() +'</td>' +
					'<td><a class="cancel-icon solo" title="'+ texts.gui['delete'] +'" href="javascript:;"></a></td>'
				);
				_after(newRow, insertAfter);
				var cancelIcon = newRow.cells[2].children[0];
				cancelIcon.addEventListener('click', function() {
					var row = this.parentNode.parentNode;
					var tBody = row.parentNode;
					var pLink = row.children[0].children[0];

					deleteColorRow(parseInt(pLink.innerHTML, 10));
				});
			}
		}
		function deleteColorRow(p) {
			if (!p || p == 100) {
				colors[p] = std[p];
				addColorRow(p, colors[p]);
				reloadColors();
			} else {
				delete colors[p];
				var colorBarRows = colorTable.rows;
				for (var i = 1, row; row = colorBarRows[i]; i++) {
					var pLink = row.children[0].children[0];
					if (parseInt(pLink.innerHTML, 10) == p)
						_remove(row);
				}
			}

			reloadColors();
		}
		function getColors() {
			return colors;
		}

		return {
			getColors: getColors
		};
	}


	var Queues = {
		units: {},
		buildings: {},
		tearDownCount: 0,
		initialized: false,
		init: function() {
			if (Data.settings.get('production_recruitment') && __('production_table')) {
				var ret = Queues.readProductionOverview();
				Queues.trimProductionOverview(ret.builds, ret.units);
			} else if (
						screen == 'main' ||
						screen == 'barracks' ||
						screen == 'stable' ||
						screen == 'garage' ||
						screen == 'train'
						) {
				var main = screen == 'main';
				var offset = main ? 2 : 0;
				Style.add('queue').set(
					'.order { float: '+ Style.left +'; margin-right: 3px; position: relative; text-align: '+ Style.left +'; width: 22px; }' +
					'.order.tearDown { width: 34px; }' +
					'.order.tearDown > img { float: '+ Style.left +'; }' +
					'.order:first-child { margin-right: 3px; width: auto; }' +
					'.order:first-child > .order-cancel-icon { left: 15px; }' +
					'.order.tearDown:first-child > .order-cancel-icon { left: 27px; }' +
					'.order_queue { list-style: none outside none; margin: 0; padding: 0; width: 100%; }' +
					'.order-status-light { height: 3px; left: 0; position: absolute; top: 0; width: 3px; z-index: 2; }' +
					'.order-cancel-icon { cursor: pointer; height: 7px; position: absolute; right: 0; top: 0; width: 7px; z-index: 2; }' +
					'.queue_icon { height: '+ (18 - offset) +'px; width: '+ (18 - offset) +'px; margin: '+ (offset / 2) +'px; float: '+ Style.left +'; }' +
					'.dsplus_timer { font-size: x-small; font-weight: bold; margin: 0 5px; position: relative; top: 4px; }' +
					'.drag { cursor: move }' +
					'.placeholder { width: 22px; float: '+ Style.left +'; }'
				);
				Queues.initQueue('buildqueue_wrap');
				Queues.initQueue('trainqueue_wrap_barracks');
				Queues.initQueue('trainqueue_wrap_stable');
				Queues.initQueue('trainqueue_wrap_garage');

				if (!main) {
					__('pop_current_label').addEventListener('DOMNodeInserted', function() {
						Queues.initQueue('trainqueue_wrap_barracks');
						Queues.initQueue('trainqueue_wrap_stable');
						Queues.initQueue('trainqueue_wrap_garage');
					}, false);
				}
				Queues.initialized = true;
			}
		},

		initQueue: function(queueID) {
			var queueEl = __(queueID);
			var queueTBody = _T(queueEl, 'tbody', 0);
			if (queueTBody && !_C(queueTBody, 'dsplus_queue_hide').length) {
				var queueInfo = Queues.analyse(queueEl);

				if (Data.settings.get('queue_option'))
					Queues.reorderQueue(queueEl, queueTBody, queueInfo);
			}
		},

		readProductionOverview: function() {
			var order_queues = _C(__('production_table'), 'order_queue');
			var builds = {};
			var units = {};
			for (var i = 0, order_queue; order_queue = order_queues[i]; i++) {
				if (order_queue.tagName != 'UL')
					continue;
				var isBuild = order_queue.id && order_queue.id.indexOf('building_order') > -1;
				var row = order_queue.parentNode.parentNode;
				var link = _T(row, 'a', 0);
				var match = link.href.match(/village=(\d+)/);
				if (!match)
					continue;
				var villageID = match[1];
				if (isBuild) {
					builds[villageID] = [];
					var titlePattern = /(.+) - (.+)/;
					var srcPattern = /buildings\/(.+)\.png/;
				} else {
					units[villageID] = {
						queue: [],
						order: [],
						short: {}
					};
					var titlePattern = /(\d+) - (.+) - (.+)/;
					var srcPattern = regex.unitKey;
				}
				var images = _T(order_queue, 'img');
				for (var j = 0, image; image = images[j]; j++) {
					var match = image.src && image.src.match(srcPattern);
					if (!match)
						continue;

					var unit = match[1];
					match = image.title && image.title.match(titlePattern);
					if (!match)
						continue;

					var order_status_light = _prev(image.parentNode);
					var red = (order_status_light.style.backgroundColor == 'red');
					if (isBuild) {
						var obj = {
							unit: unit,
							tearDown: red,
							name: match[1],
							ready: match[2]
						};
						builds[villageID].push(obj);
					} else {
						var obj = {
							unit: unit,
							num: parseInt(match[1], 10),
							decom: red,
							name: match[2],
							lastReady: match[3]
						};
						units[villageID].queue.push(obj);
						var key = unit + (red ? '_decom' : '');
						if (!units[villageID].short[key]) {
							units[villageID].order.push(key);
							units[villageID].short[key] = {
								unit: obj.unit,
								sum: obj.num,
								count: 1,
								decom: obj.decom,
								name: obj.name,
								lastReady: obj.lastReady
							};
						} else {
							units[villageID].short[key].sum += obj.num;
							units[villageID].short[key].count++;
							units[villageID].short[key].lastReady = obj.lastReady;
						}
					}
				}
			}

			return {
				builds: builds,
				units: units
			};
		},
		trimProductionOverview: function(builds, units) {
			var rows = __('production_table').rows;
			for (var i = 1, row; row = rows[i]; i++) {
				var link = _T(row, 'a', 0);
				var match = link.href.match(/village=(\d+)/);
				if (!match)
					continue;
				var villageID = match[1];
				if (!units[villageID])
					continue;

				var order_queue = _ce('ul', '', { 'class': 'order_queue', style: 'width: 10em;' });
				for (var j = 0, key; key = units[villageID].order[j]; j++) {
					var obj = units[villageID].short[key];
					var listEntry = _ce('li',
						'<div style="background-color: '+ (obj.decom ? 'red' : 'green') +';" class="order-status-light"></div>' +
						'<div class="queue_icon">' +
							'<img src="'+ image_base +'unit/unit_'+ obj.unit +'.png" class="'+ key +'" alt="" title="'+ obj.sum +' ('+ obj.count +') - '+ obj.name +' - '+ obj.lastReady +'" style="width: 16px; height: 16px;">' +
						'</div>',
						{ 'class': 'order' }
					);
					order_queue.appendChild(listEntry);
				}

				var order_queue_cell = row.cells[row.cells.length-1];
				var old_order_queue = _C(order_queue_cell, 'order_queue', 0);
				_hide(old_order_queue);
				old_order_queue.parentNode.appendChild(order_queue);
			}
		},

		readBuildingOverviewRow: function(village_row) {
			var buildChanges = {};
			var cells = village_row.cells;
			var lastCell = cells[cells.length-1];
			var lis = _T(lastCell, 'li');
			for (var i = 0, li; li = lis[i]; i++) {
				var order_status_light = li.firstElementChild;
				var down = order_status_light.style.backgroundColor == 'red';

				var buildingImg = li.lastElementChild.firstElementChild;
				var match = buildingImg.src.match(/buildings\/(.+)\.png/);
				if (!match)
					return;
				var build = match[1];
				if (!buildChanges[build])
					buildChanges[build] = 0;
				buildChanges[build] += down ? -1 : 1;
			}
			return buildChanges;
		},

		analyse: function(queueEl) {
			Queues.tearDownCount = 0;
			var isBuild = /build/.test(queueEl.id);
			var extraCost = 0;
			var queue = [];
			var order = [];
			var short = {};

			var pattern = isBuild ? texts.regex.mainQueue : texts.regex.unitQueue;
			var unitTimer;
			if (!isBuild)
				unitTimer = _C(_prev(queueEl), 'timer', 0).innerHTML;

			var rows = _T(queueEl, 'tr');
			for (var i = 1, row; row = rows[i]; i++) {
				var cells = row.cells;
				if (cells.length == 1) {
					var match = cells[0].innerHTML.match(/<b>(\d+)%<\/b>/);
					if (!extraCost && match)
						extraCost = parseInt(match[1], 10);
					continue;
				}
				var match = cells[0].textContent.match(pattern);
				if (!match)
					continue;

				var durationSpan = _T(cells[1], 'span', 0);
				var cancelLink = _T(row, 'a', 'last');
				if (isBuild) {
					var down = match[1] ? false : true;
					var build = match[1] ? match[1].trim() : match[3].trim();
					var buildKey = 'unknown';
					for (var key in lib.buildingInfo)
						if (build == texts.building[key]) {
							buildKey = key;
							break;
						}

					var timerEl = _C(row, 'timer', 0);
					var timer = timerEl && timerEl.innerHTML;
					// level (NaN for tearDown)
					var num = parseInt(match[2], 10);
					var completionCell = cells[cells.length-2];
					if (completionCell.children[0] && completionCell.children[0].tagName == 'A')
						completionCell = _prev(completionCell);
					var obj = {
						build: buildKey,
						name: build,
						num: num,
						count: 1,
						down: down,
						duration: durationSpan.textContent,
						lastReady: completionCell.textContent,
						onclick: cancelLink.getAttribute('onclick'),
						timer: timer
					};
					queue.push(obj);
					// for the templates: main: +1, hide: -3...
					var num = down ? -1*obj.num : obj.num;
					if (!short[buildKey]) {
						order.push(buildKey);
						short[buildKey] = {
							build: buildKey,
							levelUp: down ? 0 : 1,
							levelDown: down ? 1 : 0
						};
					} else {
						if (down)
							short[buildKey].levelDown++;
						else
							short[buildKey].levelUp++;
					}
					Queues.buildings[buildKey] = short[buildKey];
					if (down) Queues.tearDownCount++;
				} else {
					var unit = match[2].trim();
					var unitKey = 'unknown';
					for (var key in lib.unitInfo)
						if (unit == texts.unit[key] || unit == texts.units[key]) {
							unitKey = key;
							break;
						}

					var duration = cells[1].textContent;
					if (durationSpan)
						duration = durationSpan.textContent;
					var completionCell = cells[2];
					var obj = {
						unit: unitKey,
						name: unit,
						num: parseInt(match[1], 10),
						count: 1,
						duration: duration,
						lastReady: completionCell.textContent,
						onclick: cancelLink.getAttribute('onclick'),
						timer: unitTimer
					};
					queue.push(obj);
					if (!short[unitKey]) {
						order.push(unitKey);
						// copy the "obj"-Object
						short[unitKey] = {
							unit: unitKey,
							name: unit,
							num: obj.num,
							count: 1,
							duration: duration,
							lastReady: obj.lastReady,
							onclick: obj.onclick,
							timer: unitTimer
						};
					} else {
						short[unitKey].num += obj.num;
						short[unitKey].count++;
						short[unitKey].lastReady = obj.lastReady;
					}
					Queues.units[unitKey] = short[unitKey];
				}
			}

			return {
				extraCost: extraCost,
				queue: queue,
				order: order,
				short: short
			};
		},
		reorderQueue: function(queueEl, queueTBody, queueInfo) {
			var isBuild = /build/.test(queueEl.id);

			// finish of the last position of the production queue
			var lastReadyQueue = '<i>?</i>';
			if (queueInfo.queue.length)
				lastReadyQueue = queueInfo.queue[queueInfo.queue.length-1].lastReady;

			var createEntryHTML;
			if (isBuild) {
				createEntryHTML = function(entry, shortQueue, i, sortable) {
					var titleFormat = texts.gui.buildQueueTitleFormat;
					var cls = 'order '+ (!sortable || !i || shortQueue ? 'nodrag' : 'drag');
					var tearImg = '';
					if (entry.down) {
						titleFormat = texts.gui.tearDownTitleFormat;
						cls += ' tearDown';
						tearImg = '<img src="'+ image_base +'overview/down.png" alt="'+ texts.gui.tearDown +'">';
					}
					var title = titleFormat
						.replace('%label%', entry.name)
						.replace('%number%', entry.num)
						.replace('%count%', entry.count)
						.replace('%duration%', entry.duration)
						.replace('%finish%', entry.lastReady);
					var entryTimer = '';
					if (entry.timer)
						entryTimer = '<span class="dsplus_timer">'+ entry.timer +'</span>';

					return (
						'<li id ="buildorder_'+ i +'" class="'+ cls +'">' +
							'<div style="background-color: transparent;" class="order-status-light"></div>' +
							tearImg +
							'<div class="order-cancel-icon"><img class="" alt="" onclick="'+ entry.onclick +'" title="'+ texts.gui.cancel +'" src="'+ image_base +'delete_small.png"></div>' +
							'<div class="queue_icon">' +
								'<img src="'+ image_base +'buildings/'+ entry.build +'.png" alt="'+ entry.name +'" title="'+ title +'" class="tooltip queue_icon">' +
							'</div>' +
							entryTimer +
						'</li>'
					);
				};
			} else {
				createEntryHTML = function(entry, shortQueue, i, sortable) {
					var titleFormat = texts.gui.unitQueueTitleFormat;
					var cancelIcon = '';
					var cls = 'order '+ (!sortable || !i || shortQueue ? 'nodrag' : 'drag');
					if (shortQueue && entry.count > 1)
						titleFormat = texts.gui.shortQueueTitleFormat;
					else
						cancelIcon = '<div class="order-cancel-icon"><img class="" alt="" onclick="'+ entry.onclick +'" title="'+ texts.gui.cancel +'" src="'+ image_base +'delete_small.png"></div>';

					var title = titleFormat
								.replace('%label%', entry.name)
								.replace('%number%', entry.num)
								.replace('%count%', entry.count)
								.replace('%duration%', entry.duration)
								.replace('%finish%', entry.lastReady);
					var entryTimer = '';
					if (entry.timer)
						entryTimer = '<span class="dsplus_timer">'+ entry.timer +'</span>';

					return (
						'<li id="trainorder_'+ i +'" class="'+ cls +'">' +
							'<div style="background-color: transparent;" class="order-status-light"></div>' +
							cancelIcon +
							'<div class="queue_icon">' +
								'<img src="'+ image_base +'unit/unit_'+ entry.unit +'.png" alt="'+ entry.name +'" title="'+ title +'" class="tooltip queue_icon">' +
							'</div>' +
							entryTimer +
						'</li>'
					);
				};
			}
			var orderQueueHTML = '';
			// do not abridge the building-queue!?
			if (!isBuild && Data.settings.get('queue_option') == 2) {
				for (var i = 0, max = queueInfo.order.length; i < max; i++) {
					var entry = queueInfo.short[queueInfo.order[i]];
					if (i)
						entry.timer = 0;
					orderQueueHTML += createEntryHTML(entry, true, i, (max > 2 && lib.hasPA));
				}
			} else {
				for (var i = 0, max = queueInfo.queue.length; i < max; i++) {
					var entry = queueInfo.queue[i];
					if (i)
						entry.timer = 0;
					orderQueueHTML += createEntryHTML(entry, false, i, (max > 2 && lib.hasPA));
				}
			}

			var lastChild = queueEl.children[queueEl.children.length-1];
			var cancelAll = _C(lastChild, 'evt-confirm', 0);
			var divToPrepend = queueTBody.parentNode.parentNode;
			var tBodyClass = divToPrepend.id;
			var headText = '';
			var lastCell = '';
			if (isBuild) {
				headText = texts.gui.buildTask;
				if (queueInfo.extraCost)
					lastCell += '<img src="'+ image_base +'/gold.png" alt="" title="'+ texts.gui.extraCost +': '+ queueInfo.extraCost +'%" class="tooltip">';
			} else {
				headText = texts.gui.unitTask;
				if (cancelAll)
					lastCell += '<a href="'+ cancelAll.href +'"><img src="'+ image_base +'delete.png" alt="x" title="'+ texts.gui.cancelAll +'"></a>';
				divToPrepend = divToPrepend.parentNode;
			}
			var shortQueueTable = _ce(
				'table',
				'<tbody class="'+ tBodyClass +'">' +
					'<tr>' +
						'<th class="nowrap">'+ headText +' &minus; '+ lastReadyQueue +'</th>' +
						'<th style="text-align: center;"><a href="javascript:;" class="dsplus_queue_show">'+ lib.guiDown +'</a></th>' +
					'</tr>' +
					'<tr>'+
						'<td class="nowrap" style="padding: 4px 6px;">' +
							'<ul class="order_queue">' +
								orderQueueHTML +
							'</ul>' +
						'</td>' +
						'<td>' +
							lastCell +
						'</td>' +
					'</tr>' +
				'</tbody>',
				{ 'class': 'vis', style: 'display: none;' }
			);
			var firstChild = divToPrepend.children[0];
			if (isBuild && firstChild.tagName == 'TABLE' && firstChild.children[0].className == tBodyClass)
				return;

			_before(shortQueueTable, firstChild);
			var queue_show_link = shortQueueTable.rows[0].cells[1].children[0];
			var queue_show_click = function() {
				var par = this.parentNode;
				var firstParentTBody;
				var lastParentDiv;
				do {
					if (par.tagName == 'DIV')
						lastParentDiv = par;
					if (!firstParentTBody && par.tagName == 'TBODY')
						firstParentTBody = par;
					par = par.parentNode;
				} while (par && par.id != 'ds_body');
				var tables = _T(lastParentDiv, 'table');
				for (var i = 0, table; table = tables[i]; i++) {
					if (table.style.display == 'none')
						_show(table);
					else
						_hide(table);
				}

				var key = firstParentTBody.className;
				if (key) {
					setTimeout(function() {
						Data.setQueue(key, false);
					});
				}
			};
			queue_show_link.addEventListener('click', queue_show_click, true);

			var queue_hide_link = _ce('a', lib.guiUp, { href: 'javascript:;', 'class': 'dsplus_queue_hide' });
			var queue_hide_click = function() {
				var par = this.parentNode;
				var firstParentDiv;
				var lastParentDiv;
				do {
					if (par.tagName == 'DIV') {
						lastParentDiv = par;
						if (!firstParentDiv)
							firstParentDiv = par;
					}
					par = par.parentNode;
				} while (par && par.id != 'ds_body');
				var tables = _T(lastParentDiv, 'table');
				for (var i = 0, table; table = tables[i]; i++) {
					if (table.style.display == 'none')
						_show(table);
					else
						_hide(table);
				}

				var key = firstParentDiv.id;
				if (key) {
					setTimeout(function() {
						Data.setQueue(key, true);
					});
				}
			};
			queue_hide_link.addEventListener('click', queue_hide_click, true);

			var firstTH = queueTBody.rows[0].cells[0];
			firstTH.appendChild(_text(' '));
			firstTH.appendChild(queue_hide_link);

			// refresh events etc.
			if (Data.getQueue(queueEl.id))
				queue_hide_click.call(queue_hide_link);

			var serverTime = win.getTime(jQuery(__('serverTime')));
			var spans = _T(shortQueueTable, 'span');
			for (var i = 0, span; span = spans[i]; i++) {
				if (!span.classList.contains('dsplus_timer'))
					continue;
				var $span = jQuery(span);
				var startTime = win.getTime($span);
				if (startTime != -1)
					win.addTimer($span, serverTime + startTime, false);
			}

			if (!isBuild || Queues.initialized)
				UI.ToolTip(_C(shortQueueTable, 'tooltip'), {});

			function create_sortable(row) {
				jQuery(row).sortable({
					items: '.drag',
					handle: '.queue_icon',
					axis: 'x',
					placeholder: 'placeholder',
					forcePlaceholderSize: true,
					stop: function (event, ui) {
						if (isBuild) {
							var url = '/game.php?village='+ game_data.village.id +'&ajaxaction=buildorder_reorder&h='+ game_data.csrf +'&buildmode=1&screen=main';
							jQuery.ajax({
								dataType: 'json',
								type: 'get',
								url: url,
								data: jQuery(row).sortable('serialize'),
								success: function (data) {
									if (data.error) {
										UI.InfoMessage(data.error, 2000, true);
										jQuery(row).sortable('cancel');
										return
									};
									win.BuildingMain.init_buildqueue(url);
									win.BuildingMain.update_all(data);
								}
							});
						}
					}
				});
			}
			create_sortable(_C(shortQueueTable, 'order_queue', 0));
		}
	};


	var Filter = {
		filter: ['points', 'farm', 'distance'],
		points: {
			enabled: Data.get('filter_points_enabled'),
			value: Data.get('filter_points_val'),
			inverted: Data.get('filter_points_inverted'),
			used: false
		},
		farm: {
			enabled: Data.get('filter_farm_enabled'),
			value: Data.get('filter_farm_val'),
			inverted: Data.get('filter_farm_inverted'),
			used: false
		},
		distance: {
			enabled: Data.get('filter_distance_enabled'),
			value: Data.get('filter_distance_val'),
			inverted: Data.get('filter_distance_inverted'),
			used: false
		},
		matchCount: {
			points: 0,
			farm: 0,
			distance: 0,
			total: 0
		},
		type: '',
		observerList: [],

		isUsed: function(option) {
			return this[option].used;
		},
		isEnabled: function(option) {
			return this[option].enabled;
		},
		getValue: function(option) {
			return this[option].value;
		},
		isInverted: function(option) {
			return this[option].inverted;
		},
		isFilterActive: function() {
			for (var i = 0, option; option = this.filter[i]; i++) {
				if (this.isUsed(option) && this.isEnabled(option))
					return true;
			}
			return false;
		},
		setEnabled: function(option, enabled) {
			this[option].enabled = enabled;
			Data.set('filter_'+ option +'_enabled', enabled);
			Data.save();
			this.apply();
		},
		setValue: function(option, value) {
			value = parseInt(value, 10) || 0;
			this[option].value = value;
			Data.set('filter_'+ option +'_val', value);
			Data.save();
			this.apply();
		},
		setInverted: function(option, inverted) {
			this[option].inverted = inverted;
			Data.set('filter_'+ option +'_inverted', inverted);
			Data.save();
			this.apply();
		},
		setOpen: function(openStatus) {
			Data.set('filter_open', openStatus);
			Data.save();
		},

		initOverview: function(type) {
			this.type = type;
			if (type != 'combined') {
				this.points.used = true;
			}
			if (type == 'production') {
				this.farm.used = true;
			}
			this.distance.used = true;

			this.apply(true);
		},

		apply_points: function(row, cell) {
			var value = parseInt(cell.innerHTML.replace(/(<[^>]*>)|\.|\s/g, ''), 10);
			if (!this.isInverted('points') && value <= this.getValue('points') || this.isInverted('points') && value > this.getValue('points')) {
				return true;
			}
			return false;
		},
		apply_farm: function(row, cell) {
			var vals = cell.innerHTML.replace(/(<[^>]*>)|\.|\s/g, '').split('/');
			var value = parseInt(vals[1], 10) - parseInt(vals[0], 10);
			if (!this.isInverted('farm') && value <= this.getValue('farm') || this.isInverted('farm') && value > this.getValue('farm')) {
				return true;
			}
			return false;
		},
		apply_distance: function(row, cell) {
			var span = _T(cell, 'span', 1);
			if (!span)
				return false;
			var match = span.textContent.match(texts.regex.villageCoords);
			if (!match)
				return false;
			var coords = [match[1], match[2]];
			var value = lib.getCoordsDiff(0, coords, 0);
			if (!this.isInverted('distance') && value <= this.getValue('distance') || this.isInverted('distance') && value > this.getValue('distance')) {
				return true;
			}
			return false;
		},
		apply: function(initialize) {
			var table = __(this.type +'_table');
			if (!table)
				return;

			var idx = {};
			var rows = table.rows;
			var firstTH = _T(table, 'th', 0);
			var headerRow = firstTH.parentNode;
			var headerCells = headerRow.cells;
			for (var i = 0, th; th = headerCells[i]; i++) {
				var href = 0;
				if (th.children.length)
					href = th.children[0].href;

				if (href && /[\?&]order=points(&|$)/.test(href)) {
					idx.points = i;
				} else if (href && /[\?&]order=pop(&|$)/.test(href)) {
					idx.farm = i;
				} else if (href && /[\?&]order=name(&|$)/.test(href)) {
					idx.distance = i;
				}
			}
			for (var i = 0, filterOption; filterOption = this.filter[i]; i++) {
				this.matchCount[filterOption] = 0;
				if (this.isUsed(filterOption) && !idx.hasOwnProperty(filterOption))
					return;
			}

			this.matchCount.total = 0;
			for (var r = 1, row; row = rows[r]; r++) {
				var cells = row.cells;
				if (!cells.length || !_T(row, 'td').length)
					continue;

				if (!row.id || row.id.indexOf('group_edit_tr_') == -1)
					_show(row);

				var showRow = true;
				for (var i = 0, filterOption; filterOption = this.filter[i]; i++) {
					if (!this.isUsed(filterOption) || !this.isEnabled(filterOption) || cells.length <= idx[filterOption])
						continue;

					var match = this['apply_'+ filterOption](row, cells[idx[filterOption]]);
					if (match) {
						_hide(row);
						showRow = false;
					} else {
						this.matchCount[filterOption]++;
					}
				}
				if (showRow)
					this.matchCount.total++;
			}

			if (this.observerList.length) {
				for (var i = 0, observer; observer = this.observerList[i]; i++) {
					observer(initialize);
				}
			}
		}
	};


	var Settings = {
		options: {
			/* types:
			 *	0 = text
			 *	1 = checkbox
			 *	2 = input
			 *	3 = select
			 *	4 = special
			 */
			  consider_successratio:		{ type: 1, advanced: false }
			, sitting_enabled:				{ type: 1, advanced: false }
			, storageColor_enabledAt:		{ type: 4, advanced: true  }
			, storageColor_bar:				{ type: 4, advanced: true  }
			, farmlist_option:				{ type: 3, advanced: false }
			, farmlist_attackedFarms:		{ type: 3, advanced: false }
			, farming_farmSpys:				{ type: 2, advanced: false, enableCheckbox: true, showStdValue: true, isInt: true }
			, farming_spyOnlyWithUnits:		{ type: 1, advanced: true  }
			, farming_sendRams:				{ type: 2, advanced: false, enableCheckbox: true, showStdValue: true, isInt: true }
			, farming_filter_text:			{ type: 0 }
			, farming_minRes:				{ type: 2, advanced: false, enableCheckbox: true, showStdValue: true, isInt: true }
			, farming_maxDistance:			{ type: 2, advanced: false, enableCheckbox: true, showStdValue: true, isFloat: true }
			, farming_maxAge:				{ type: 2, advanced: false, enableCheckbox: true, showStdValue: true, isFloat: true }
			, farming_maxRuntime:			{ type: 2, advanced: false, enableCheckbox: true, showStdValue: true, isFloat: true }
			, bot_enabled:					{ type: 1, advanced: false }
			, bot_farmOver:					{ type: 3, advanced: false }
			, bot_fa_click:					{ type: 3, advanced: true  }
			, place_showUnitSelection:		{ type: 1, advanced: true  }
			, farming_calcUnitNumbers:		{ type: 3, advanced: false }
			, farming_unitPreferences:		{ type: 4, advanced: false }
			, farming_sendCatas:			{ type: 1, advanced: false }
			, farming_buildingPreferences:	{ type: 4, advanced: false }
			, map_overlays_enabled:			{ type: 1, advanced: false }
			, reports_searchInput:			{ type: 1, advanced: true  }
			, reports_changeLinkStyle:		{ type: 1, advanced: true  }
			, reports_dot_text:				{ type: 0, advanced: true  }
			, reports_newReport:			{ type: 4, advanced: true  }
			, reports_readReport:			{ type: 4, advanced: true  }
			, reports_spyReport:			{ type: 4, advanced: true  }
			, reports_oldReport:			{ type: 4, advanced: true  }
			, reports_reorder:				{ type: 1, advanced: true  }
			, reports_showSurvivor:			{ type: 1, advanced: false }
			, reports_showBashPoints:		{ type: 1, advanced: false }
			, freePA:						{ type: 1, advanced: false }
			, fileSite:						{ type: 3, advanced: false }
			, showStats:					{ type: 1, advanced: true  }
			, statSite:						{ type: 3, advanced: false }
			, quickbar:						{ type: 4, advanced: false }
			, fa_text:						{ type: 0, advanced: true  }
			, fa_additions_enabled:			{ type: 1, advanced: true  }
			, fa_skipAttackedVillages:		{ type: 1, advanced: true  }
			, fa_filter_minRes:				{ type: 2, advanced: true, enableCheckbox: true, showStdValue: true, addFilterHint: true, isInt: true }
			, fa_filter_maxDistance:		{ type: 2, advanced: true, enableCheckbox: true, showStdValue: true, addFilterHint: true, isFloat: true }
			, hotkeys_enabled:				{ type: 1, advanced: false }
			, hotkeys_display:				{ type: 1, advanced: true  }
			, hotkeys_coordSelector:		{ type: 1, advanced: true  }
			, userKeys:						{ type: 4, advanced: false }
			, overview_showBooty:			{ type: 1, advanced: true  }
			, template_units_enabled:		{ type: 1, advanced: false }
			, template_buildings_enabled:	{ type: 1, advanced: false }
			, queue_option:					{ type: 3, advanced: false }
			, production_recruitment:		{ type: 1, advanced: false }
			, production_removeCols:		{ type: 1, advanced: false }
			, production_filter_enabled:	{ type: 1, advanced: true  }
			, fixQuickbar:					{ type: 1, advanced: true  }
			, fixInfobar:					{ type: 1, advanced: true  }
			, confirm_deleting:				{ type: 1, advanced: true  }
			, useEnhancedAnimations:		{ type: 1, advanced: true  }
		}
		, parseValue: function(key, value)
		{
			try
			{
				if (Settings.options[key].isInt)
				{
					value = parseInt(value, 10);
				}
				else if (Settings.options[key].isIntFloat)
				{
					if (value.indexOf(',') > -1 && value.indexOf('.') == -1)
						value = value.replace(',', '.');
					value = parseFloat(value);
				}
			} catch(error) {
				value = Data.settings.getStd(key);
				if (Settings.options[key].enableCheckbox)
					value = value[1];
			}
			return value;
		}
		, placeForm: function()
		{
			if (!Data.settings.get('place_showUnitSelection')) return;

			var unit_enabled = jQuery.extend({}, Data.settings.get('farming_unitPreferences'), {
				spy: [Data.settings.get('farming_farmSpys', 0), 0],
				ram: [Data.settings.get('farming_sendRams', 0), 0],
				catapult: [Data.settings.get('farming_sendCatas'), 0]
			});
			var titles = {
				spy: function()
				{
					if (Data.settings.get('farming_farmSpys', 0))
					{
						return texts.settings.farming_farmSpys +' '+ Data.settings.get('farming_farmSpys', 1) + texts.settings.farming_farmSpys_after;
					}
					return '';
				}
				, ram: function()
				{
					if (Data.settings.get('farming_sendRams', 0))
					{
						return texts.settings.farming_sendRams +' '+ Data.settings.get('farming_sendRams', 1);
					}
					return '';
				}
			};

			var clickCheckbox = function()
			{
				var input = _C(this.parentNode, 'unitsInput', 0);
				var match = input && input.id.match(/unit_input_(.+)/);
				if (!match)
					return;
				var unit = match[1];
				var checked = this.checked;
				if (unit == 'spy')
					Data.settings.set('farming_farmSpys', 0, checked);
				else if (unit == 'ram')
					Data.settings.set('farming_sendRams', 0, checked);
				else if (unit == 'catapult')
					Data.settings.set('farming_sendCatas', checked);
				else
					Data.settings.set('farming_unitPreferences', unit, [checked, Data.settings.get('farming_unitPreferences', unit)[1]]);
				Data.settings.save();
				GUI.settings.colorInput(__('unit_input_'+ unit));

				if (titles[unit])
					this.title = titles[unit]();
			};
			for (var unit in lib.unitInfo)
			{
				var input = __('unit_input_'+ unit);
				if (!input)
					continue;
				var checkbox = _ce('input', '', {
					'type': 'checkbox'
				});
				_prepend(checkbox, input.parentNode);
				if (titles[unit])
				{
					checkbox.title = titles[unit]();
				}
				if (unit_enabled.hasOwnProperty(unit))
				{
					checkbox.checked = unit_enabled[unit][0];
					checkbox.addEventListener('click', clickCheckbox, false);
				}
				else
				{
					checkbox.disabled = true;
				}

				GUI.settings.colorInput(input);
			}
		}
		, optionsToCheck: [],
		save: function() {
			var success = true;
			for (var i = 0, key; key = Settings.optionsToCheck[i]; i++) {
				var option = Settings.options[key];
				if (option.type == 1) {
					var checked = __('dsplus_'+ key).checked;
					Data.settings.set(key, checked);
				} else if (option.type == 2) {
					var checked = __('dsplus_'+ key +'_enabled').checked;
					var val = Settings.parseValue(key, __('dsplus_'+ key).value);
					Data.settings.set(key, [checked, val]);
				} else if (option.type == 3) {
					var selectedIndex = __('dsplus_'+ key).selectedIndex;
					Data.settings.set(key, selectedIndex);
				} else if (option.type == 4) {
					if (Settings.hasOwnProperty('save_'+ key))
						success = eval('Settings.save_'+ key +'()') && success;
				}
			}

			if (success) {
				Data.settings.save();
				lib.success(texts.gui.saved);
			}
			return success;
		},
		saveClose: function() {
			if (Settings.save())
				location.reload();
		},
		doExport: function() {
			var request = {};
			var keys = ['config', 'templates', 'reports_villages_coords', 'commands', 'booty', 'misc'];
			var somethingToExport = false;
			for (var i = 0, key; key = keys[i]; i++) {
				if (key == 'reports_villages_coords') {
					request[key] = __('dsplus_export_reports').checked && __('dsplus_export_villages').checked && __('dsplus_export_coords').checked;
				} else {
					request[key] = __('dsplus_export_'+ key).checked;
				}
				somethingToExport = somethingToExport || request[key];
			}
			if (!somethingToExport) {
				lib.error(texts.settings.export.nothingSelected);
				return;
			}

			var loading = GUI.loading('export_loading');
			var export_button = __('dsplusExport');
			export_button.disabled = true;
			var exportData = Data.exportIt(request);

			lib.ajaxFunc('POST', 'export', exportData, true, {
				onreadystatechange: function() {
					if (this.readyState == 4) {
						_remove(loading);
						export_button.disabled = false;

						var responseText = this.responseText;
						if (this.status == 200 && responseText) {
							var export_code = __('dsplus_export_code');
							export_code.value = responseText;
							export_code.select();

							lib.success(texts.settings.export.success);
						} else {
							console.log('Response:', this.response);

							lib.error(texts.settings.export.error);
						}
					}
				}
			});
		},
		doImport: function() {
			var loading = GUI.loading('import_loading');

			var import_button = __('dsplusImport');
			import_button.disabled = true;
			var importData = { code: __('dsplus_import_code').value.trim() };

			lib.ajaxFunc('POST', 'import', importData, true, {
				onreadystatechange: function() {
					if (this.readyState == 4) {
						_remove(loading);

						var error = true;
						var responseText = this.responseText;
						if (this.status == 200 && responseText) {
							var ret = Data.importIt(responseText);
							for (var type in ret) {
								if (type == 'success' || type == 'empty' || !texts.settings.port.hasOwnProperty(type))
									continue;

								var el = __('dsplus_import_'+ type);
								el.checked = !!ret[type];
								var textEl = _next(el.parentNode);
								if (ret[type]) {
									textEl.style.color = 'green';
									textEl.style.fontWeight = '';
								} else {
									textEl.style.color = 'red';
									textEl.style.fontWeight = 'bold';
								}
							}
							if (ret.success && !ret.empty) {
								lib.success(texts.settings.import.success);

								__('dsplus_import_code').value = '';
								import_button.disabled = false;
								error = false;
							}
						} else {
							console.log('Response:', this.response);
						}
						if (error) {
							// delay enabling the import button:
							setTimeout(function() {
								import_button.disabled = false;
							}, 2000);

							lib.error(texts.settings.import.error);
						}
					}
				}
			});
		},
		doReset: function() {
			var request = {
				config: __('dsplus_reset_config').checked,
				templates: __('dsplus_reset_templates').checked,
				reports_villages_coords: __('dsplus_reset_reports').checked && __('dsplus_reset_villages').checked && __('dsplus_reset_coords').checked,
				commands: __('dsplus_reset_commands').checked,
				booty: __('dsplus_reset_booty').checked
			};
			var somethingToReset = request.config || request.templates || request.reports_villages_coords || request.commands || request.booty;
			if (!somethingToReset) {
				lib.error(texts.settings.reset.nothingSelected);
				return false;
			}

			Data.resetIt(request);
			lib.alert(texts.settings.reset.complete);
			return true;
		},

		// general
		make_storageColor_enabledAt: function(text, optionContainer) {
			var cellTitle = _ce('b', text +':');
			optionContainer.appendChild(cellTitle);

			var table = _ce('table', '', {
				'style': 'margin-left: 20px;'
			});
			var tBody = _ce('tbody');
			table.appendChild(tBody);
			var options = texts.settings.storageColor_enabledAt_texts;
			var all = true;
			for (var key in options) {
				if (key == 'all')
					continue;

				var optionRow = tBody.insertRow(-1);
				var checkboxCell = optionRow.insertCell(-1);
				var enableCheckbox = GUI.settings.createCheckbox('dsplus_storageColor_enabledAt_'+ key, Data.settings.get('storageColor_enabledAt', key));
				checkboxCell.appendChild(enableCheckbox);
				var textCell = optionRow.insertCell(-1);
				textCell.innerHTML = options[key];
				if (!Data.settings.get('storageColor_enabledAt', key))
					all = false;
			}

			var spacerRow = tBody.insertRow(-1);
			spacerRow.innerHTML = '<th colspan="2"></th>';

			var allRow = tBody.insertRow(-1);
			var checkboxCell = allRow.insertCell(-1);
			var enableAllCheckbox = GUI.settings.createCheckbox('dsplus_storageColor_enabledAt_all', all);
			checkboxCell.appendChild(enableAllCheckbox);
			var textCell = allRow.insertCell(-1);
			textCell.innerHTML = '<i>'+ options.all +'</i>';

			optionContainer.appendChild(table);
		},
		save_storageColor_enabledAt: function() {
			var keys = texts.settings.storageColor_enabledAt_texts;
			for (var key in keys) {
				if (key != 'all') {
					var status = __('dsplus_storageColor_enabledAt_'+ key).checked;
					Data.settings.set('storageColor_enabledAt', key, status);
				}
			}
			return true;
		},

		storageColorBar: null,
		make_storageColor_bar: function(text, optionContainer) {
			optionContainer.id = 'dsplus_storageColorBar_frame';
		},
		save_storageColor_bar: function() {
			Data.settings.set('storageColors', Settings.storageColorBar.getColors());
			return true;
		},


		// general-attach
		attach_general: function() {
			// storageColor_enableAt
			var storageEnabledAll = __('dsplus_storageColor_enabledAt_all');
			var optionContainer = storageEnabledAll.parentNode;
			while (!optionContainer.classList.contains('dsplus_option_container'))
				optionContainer = optionContainer.parentNode;

			optionContainer.addEventListener('click', function(event) {
				var target = event.target;
				if (target.tagName == 'INPUT' && target.type == 'checkbox') {
					var clickedCheckbox = target.id.replace('dsplus_storageColor_enabledAt_', '');
					var alterAll = (clickedCheckbox == 'all');
					var keys = texts.settings.storageColor_enabledAt_texts;
					var allChecked = true;
					for (var key in keys) {
						var checkbox = __('dsplus_storageColor_enabledAt_'+ key);
						if (alterAll && key != 'all') {
							// check all
							checkbox.checked = target.checked;
						} else if (key != 'all') {
							// are all checked?
							allChecked = allChecked && checkbox.checked;
						}
					}

					if (!alterAll)
						storageEnabledAll.checked = allChecked;
				}
			});

			// storageColorBar
			Settings.storageColorBar = ColorBar('general_storage', __('dsplus_storageColorBar_frame'), Data.settings.get('storageColors'), Data.settings.getStd('storageColors'));

		},

		// place
		make_farming_unitPreferences: function(text, optionContainer) {
			optionContainer.style.display = 'table-row';
			var HTML = '<div style="display: table-cell; vertical-align: top; padding: 2px 3px;">'+ text +':</div>' +
				'<div style="display: table-cell;"><table class="vis preference_table"><tbody id="dsplus_farming_unitPreferences">';
			var unit_order = Data.settings.get('farming_unitPreferences_order');
			var unit_values = Data.settings.get('farming_unitPreferences');
			for (var i = 0, max = unit_order.length; i < max; i++) {
				var unit = unit_order[i];
				if (!lib.unitInfo[unit])
					continue;
				var checked = unit_values[unit][0];
				var cellType = checked ? 'th' : 'td';
				HTML += '<tr>' +
					'<'+ cellType +'>' +
						'<table class="nowrap"><tr>' +
							'<td><input type="checkbox"'+ (checked ? ' checked' : '') +'></td>' +
							'<td><img src="'+ image_base +'unit/unit_'+ unit +'.png" alt=""></td>' +
							'<td>'+ GUI.settings.createInput('dsplus_farming_unit_'+ unit, unit_values[unit][1], 'text-align: center;', 'size="5"', (checked ? '' : 'disabled')) +'</td>' +
						'</tr></table>' +
					'</'+ cellType +'>' +
					'<td style="background-color: transparent;"><div style="width: 11px; height: 11px; background-image: url('+ image_base +'sorthandle.png); cursor: pointer;" class="bqhandle"> </div></td>' +
				'</tr>';
			}
			HTML += '</tbody></table></div>';
			optionContainer.innerHTML = HTML;
		},
		save_farming_unitPreferences: function() {
			var order = [];
			var values = {};
			var rows = __('dsplus_farming_unitPreferences').rows;
			for (var i = 0, row; row = rows[i]; i++) {
				var firstImg = _T(row, 'img', 0);
				var match = firstImg && firstImg.src.match(/unit_(.+)\.png/);
				if (!match)
					return;
				var unit = match[1];
				order.push(unit);
				var textInpupt = row.querySelector('input[type="text"]');
				var val = textInpupt.value || 0;
				try {
					val = parseInt(val, 10);
				} catch(error) {
					val = 0;
				}
				var checkbox = row.querySelector('input[type="checkbox"]');
				var checked = checkbox.checked;
				values[unit] = [checked, val];
			}
			Data.settings.set('farming_unitPreferences_order', order);
			Data.settings.set('farming_unitPreferences', values);
			return true;
		},
		make_farming_buildingPreferences: function(text, optionContainer) {
			var HTML = '<span>'+ text +':</span>' +
				'<table class="vis preference_table"><tbody id="dsplus_farming_buildingPreferences"><tr>';
			var build_order = Data.settings.get('farming_buildingPreferences_order');
			var build_enabled = Data.settings.get('farming_buildingPreferences');
			for (var i = 0, max = build_order.length; i < max; i++) {
				var build = build_order[i];
				if (!lib.buildingInfo[build])
					continue;
				var cell = build_enabled[build] ? 'th' : 'td';
				HTML += '<td style="padding: 0; background: none;" title="'+ texts.building[build] +'"><table class="vis preference_table" style="border-spacing: 0 2px;">' +
					'<tr><'+ cell +'><input type="checkbox"'+ (build_enabled[build] ? ' checked' : '') +'></'+ cell +'></tr>' +
					'<tr><th style="width: 20px; height: 22px;"><img src="'+ image_base +'buildings/'+ build +'.png" alt=""></th></tr>' +
					'<tr><td style="background-color: transparent;"><div style="margin: 0 auto;" class="bqhandle"> </div></td></tr>' +
				'</table></td>';
			}
			HTML += '</tr></tbody></table>';
			optionContainer.innerHTML = HTML;
		},
		save_farming_buildingPreferences: function() {
			var order = [];
			var active = {};
			var cells = __('dsplus_farming_buildingPreferences').rows[0].cells;
			for (var i = 0, cell; cell = cells[i]; i++) {
				var firstImg = _T(cell, 'img', 0);
				var match = firstImg && firstImg.src.match(/buildings\/(.+)\.png/);
				if (!match)
					return;
				var build = match[1];
				order.push(build);
				var checked = _T(cell, 'input', 0).checked;
				active[build] = checked;
			}
			Data.settings.set('farming_buildingPreferences_order', order);
			Data.settings.set('farming_buildingPreferences', active);
			return true;
		},
		// place-attach
		attach_farming: function() {
			_remove(__('dsplus_bot_farmOver').lastElementChild);


			// unitPreferences
			var unitPreferences = __('dsplus_farming_unitPreferences');
			jQuery(unitPreferences).sortable({ axis: 'y', handle: '.bqhandle' });
			var checkboxClick = function() {
				var oldCell = this;
				do {
					oldCell = oldCell.parentNode;
				} while (oldCell && oldCell.tagName != 'TABLE');
				oldCell = oldCell.parentNode;

				var checked = this.checked;
				var tagName = checked ? 'th' : 'td';
				var newCell = _ce(tagName);
				// move the content from the old cell to the new one
				newCell.appendChild(oldCell.children[0]);
				_before(newCell, oldCell);
				_remove(oldCell);

				var numInput = _T(newCell, 'input', 'last');
				if (numInput) {
					if (checked)
						numInput.classList.remove('disabled');
					else
						numInput.classList.add('disabled');
				}
			};
			var inputClick = function() {
				if (this.value == '0') {
					this.select();
				}
			};
			var inputs = _T(unitPreferences, 'input');
			for (var i = 0, input; input = inputs[i]; i++) {
				if (input.type == 'checkbox') {
					input.addEventListener('click', checkboxClick, false);
				} else {
					input.addEventListener('click', inputClick, false);
				}
			}

			// buildingPreferences
			var buildingPreferences = __('dsplus_farming_buildingPreferences');
			jQuery(buildingPreferences).sortable({ axis: 'x', handle: '.bqhandle', items: '> tr > td' });
			var checkboxClick = function() {
				var oldCell = this.parentNode;
				var tagName = this.checked ? 'th' : 'td';
				var newCell = _ce(tagName);
				// copy all the content of this to newCell:
				var content = oldCell.childNodes;
				for (var i = 0, el; el = content[i]; i++) {
					newCell.appendChild(el);
				}
				_before(newCell, oldCell);
				_remove(oldCell);
			};
			var inputs = _T(buildingPreferences, 'input');
			for (var i = 0, input; input = inputs[i]; i++) {
				if (input.type == 'checkbox')
					input.addEventListener('click', checkboxClick, false);
			}
		},


		// reports
		createDot: function(text, optionContainer, type) {
			optionContainer.colSpan = 1;
			var captionCell = optionContainer.parentNode.insertCell(0);
			captionCell.classList.add('caption');
			captionCell.innerHTML = text +':';

			var value = Data.settings.get('reports_'+ type +'Report');
			var hiddenInput = _ce('input', '', {
				'type': 'hidden',
				'id': 'dsplus_reports_'+ type +'Report',
				'value': value
			});
			optionContainer.appendChild(hiddenInput);
			for (var key in texts.settings.colors) {
				var img = _ce('img', '', {
					'class': 'dot',
					'src': image_base +'dots/'+ key +'.png',
					'alt': '&nbsp;',
					'title': texts.settings.colors[key]
				});
				if (key == value)
					img.classList.add('selected');

				optionContainer.appendChild(img);
			}
		},

		make_reports_newReport: function(text, optionContainer) {
			return Settings.createDot(text, optionContainer, 'new');
		},
		save_reports_newReport: function() {
			Data.settings.set('reports_newReport', __('dsplus_reports_newReport').value);
			return true;
		},

		make_reports_readReport: function(text, optionContainer) {
			return Settings.createDot(text, optionContainer, 'read');
		},
		save_reports_readReport: function() {
			Data.settings.set('reports_readReport', __('dsplus_reports_readReport').value);
			return true;
		},

		make_reports_spyReport: function(text, optionContainer) {
			return Settings.createDot(text, optionContainer, 'spy');
		},
		save_reports_spyReport: function() {
			Data.settings.set('reports_spyReport', __('dsplus_reports_spyReport').value);
			return true;
		},

		make_reports_oldReport: function(text, optionContainer) {
			return Settings.createDot(text, optionContainer, 'old');
		},
		save_reports_oldReport: function() {
			Data.settings.set('reports_oldReport', __('dsplus_reports_oldReport').value);
			return true;
		},
		// reports-attach
		attach_reports: function() {
			__('dsplus_reports_tab').addEventListener('click', function(e) {
				var target = e.target;
				if (target.tagName == 'IMG' && target.classList.contains('dot')) {
					var cell = target.parentNode;
					var selected = _C(cell, 'selected', 0);
					selected.classList.remove('selected');
					target.classList.add('selected');
					// write selected image in hidden input
					var valueInput = _T(cell, 'input', 0);
					var match = target.src.match(/graphic\/dots\/(\w+)\.png/);
					valueInput.value = match[1];
				}
			}, false);
		},

		quickbar_addedData: [],
		quickbar_editData: {},
		make_quickbar: function(text, optionContainer) {
			optionContainer.appendChild(_ce('b', text));

			var quickbarTexts = texts.settings.quickbar_texts;
			var addEntry = _ce('p', '<a href="javascript:;" id="dsplus_quickbar_addLink">'+ lib.guillemetRight +' '+ quickbarTexts.addNewLink +'</a>');
			optionContainer.appendChild(addEntry);
			var addBR = _ce('p', '<a href="javascript:;" id="dsplus_quickbar_addLinebreak">'+ lib.guillemetRight +' '+ quickbarTexts.addLinebreak +'</a>');
			optionContainer.appendChild(addBR);

			var quickbarTable = _ce('table', '', {
				'cellspacing': 0,
				'class': 'vis',
				'style': 'border-collapse: separate; border-spacing: 2px; empty-cells: show !important;'
			});
			var tHead = quickbarTable.createTHead();
			tHead.innerHTML = '<tr><th>'+ quickbarTexts.link +'</th><th colspan="3"></th></tr>';
			var tBody = _ce('tbody', '', {
				'id': 'dsplus_quickbar'
			});
			quickbarTable.appendChild(tBody);

			for (var i = 0, max = Data.quickbar.getMax(); i < max; i++) {
				var row = tBody.insertRow(-1);
				row.id = 'dsplus_quickbar_'+ i;

				var isLinebreak = Data.quickbar.isLinebreak(i);
				if (isLinebreak) {
					addLinkData(row, true);
				} else {
					addLinkData(row, false, {
						name: Data.quickbar.getName(i),
						title: Data.quickbar.getTitle(i),
						imgSrc: formatImgSrc(Data.quickbar.getImg(i)),
						target: formatTarget(Data.quickbar.getTarget(i)),
						newTab: Data.quickbar.isNewTab(i)
					});
				}
			}
			jQuery(tBody).sortable({ axis: 'y', handle: '.bqhandle' });
			optionContainer.appendChild(quickbarTable);

			var editContainer = _ce('div', '', {
				'id': 'dsplus_quickbar_manage',
				'style': 'display: none;'
			});

			var addH4 = _ce('h4', quickbarTexts.addNewLink, {
				'id': 'dsplus_quickbar_addH4',
			});
			editContainer.appendChild(addH4);
			var editH4 = _ce('h4', quickbarTexts.editLink, {
				'id': 'dsplus_quickbar_editH4'
			});
			editContainer.appendChild(editH4);
			var editId = _ce('input', '', {
				'type': 'hidden'
			});
			editContainer.appendChild(editId);
			var table = _ce('table', '', {
				'class': 'vis'
			});

			var nameRow = table.insertRow(-1);
			nameRow.insertCell(-1).innerHTML = quickbarTexts.entryName +':';
			var nameInput = GUI.settings.createInputEl('dsplus_quickbar_edit_name', '', {
				'size': 40
			});
			nameRow.insertCell(-1).appendChild(nameInput);

			var titleRow = table.insertRow(-1);
			titleRow.insertCell(-1).innerHTML = quickbarTexts.hoverText +':';
			var titleInput = GUI.settings.createInputEl('dsplus_quickbar_edit_title', '', {
				'size': 40
			});
			titleRow.insertCell(-1).appendChild(titleInput);

			var imgRow = table.insertRow(-1);
			imgRow.insertCell(-1).innerHTML = quickbarTexts.imageURL +':';
			var imgChooseCell = imgRow.insertCell(-1);
			var imgInput = GUI.settings.createInputEl('image_url', '', {
				'class': 'ar_url',
				'size': 40
			});
			imgChooseCell.appendChild(imgInput);

			var targetRow = table.insertRow(-1);
			targetRow.insertCell(-1).innerHTML = quickbarTexts.targetURL +':';
			var targetTextarea = _ce('textarea', '', {
				'class': 'ar_url',
				'rows': 4,
				'cols': 35
			});
			targetRow.insertCell(-1).appendChild(targetTextarea);

			var newTabRow = table.insertRow(-1);
			var newTabCell = newTabRow.insertCell(-1);
			newTabCell.colSpan = 2;
			var label = _ce('label');
			var newTabCheckbox = GUI.settings.createCheckbox('');
			label.appendChild(newTabCheckbox);
			label.appendChild(_text(' '+ quickbarTexts.newTab));
			newTabCell.appendChild(label);

			var btnRow = table.insertRow(-1);
			var btnCell = btnRow.insertCell(-1);
			btnCell.colSpan = 2;
			var okBtn = _ce('input', '', {
				'id': 'dsplus_quickbar_okBtn',
				'class': 'btn',
				'type': 'button',
				'value': texts.gui.OK
			});
			btnCell.appendChild(okBtn);
			var cancelBtn = _ce('input', '', {
				'id': 'dsplus_quickbar_cancelBtn',
				'class': 'btn',
				'type': 'button',
				'value': texts.gui.cancel
			});
			btnCell.appendChild(cancelBtn);

			editContainer.appendChild(table);
			optionContainer.appendChild(editContainer);

			optionContainer.addEventListener('click', function(e) {
				var target = e.target;
				if (target.id == 'dsplus_quickbar_addLink') {
					nameInput.value = '';
					titleInput.value = '';
					imgInput.value = '';
					targetTextarea.value = '';
					newTabCheckbox.checked = false;

					_show(addH4);
					_hide(editH4);
					_show(editContainer);
				} else if (target.id == 'dsplus_quickbar_addLinebreak') {
					var newRow = tBody.insertRow(-1);
					var newI = Settings.quickbar_addedData.push(0) - 1;
					newRow.id = 'dsplus_quickbar_new_'+ newI;

					addLinkData(newRow, true);
				} else if (target.id == 'dsplus_quickbar_okBtn') {
					var data = {
						name: nameInput.value,
						title: titleInput.value,
						imgSrc: imgInput.value,
						target: targetTextarea.value,
						newTab: newTabCheckbox.checked
					};
					if (addH4.style.display == 'none') {
						var i = editId.value;
						if (i.indexOf('new_') == 0) {
							i = i.replace('new_', '');
							Settings.quickbar_addedData[i] = data;
						} else {
							Settings.quickbar_editData[i] = data;
						}
					} else {
						var newRow = tBody.insertRow(-1);
						var newI = Settings.quickbar_addedData.push(data) - 1;
						newRow.id = 'dsplus_quickbar_new_'+ newI;

						addLinkData(newRow, false, {
							name: data.name,
							title: data.title,
							imgSrc: formatImgSrc(data.imgSrc),
							target: formatTarget(data.target),
							newTab: data.newTab
						});
					}
					_hide(editContainer);
				} else if (target.id == 'dsplus_quickbar_cancelBtn') {
					_hide(editContainer);
				} else if (target.classList.contains('quickbar_edit_link')) {
					var row = target.parentNode.parentNode;
					var i = row.id.replace('dsplus_quickbar_', '');
					editId.value = i;
					var data;
					if (i.indexOf('new_') == 0) {
						i = i.replace('new_', '');
						data = Settings.quickbar_addedData[i];
					} else if (Settings.quickbar_editData[i]) {
						data = Settings.quickbar_editData[i];
					} else {
						data = {
							name: Data.quickbar.getName(i),
							title: Data.quickbar.getTitle(i),
							imgSrc: formatImgSrc(Data.quickbar.getImg(i)),
							target: formatTarget(Data.quickbar.getTarget(i)),
							newTab: Data.quickbar.isNewTab(i)
						};
					}
					nameInput.value = data.name;
					titleInput.value = data.title;
					imgRow.value = data.imgSrc;
					targetTextarea.value = data.target;
					newTabCheckbox.checked = data.newTab;

					_hide(addH4);
					_show(editH4);
					_show(editContainer);
				} else if (target.classList.contains('quickbar_delete_link')) {
					var row = target.parentNode.parentNode;
					_remove(row);
				}
			}, false);

			function formatImgSrc(imgSrc) {
				return imgSrc.replace('{graphic}', image_base);
			}
			function formatTarget(target) {
				return target.replace('{game}', game_data.link_base_pure.replace('screen=', ''))
			}
			function addLinkData(row, isLinebreak, data) {
				var entryCell = row.insertCell(-1);
				entryCell.setAttribute('style', 'width:180px; max-width:180px; overflow:hidden; white-space:nowrap');
				if (isLinebreak) {
					var linebreak = _ce('hr', '', {
						'style': 'width: 100%; min-width: 180px;'
					});
					entryCell.appendChild(linebreak);
				} else {
					var link = _ce('a', '', {
						'href': data.target
					});
					if (data.newTab)
						link.target = '_blank';
					var imgLink = link.cloneNode();
					imgLink.innerHTML = '<img style="vertical-align:middle" alt="'+ data.title +'" src="'+ data.imgSrc +'">';
					entryCell.appendChild(imgLink);
					entryCell.appendChild(_text(' '));

					var textSpan = _ce('span', '', {
						'style': 'vertical-align: middle;'
					});
					var textLink = link.cloneNode();
					textLink.innerHTML = data.name;
					textSpan.appendChild(textLink);
					entryCell.appendChild(textSpan);
				}

				var editCell = row.insertCell(-1);
				if (!isLinebreak) {
					editCell.innerHTML = '<a href="javascript:;" class="quickbar_edit_link">'+ quickbarTexts.edit +'</a>';
				}

				var deleteCell = row.insertCell(-1);
				deleteCell.innerHTML = '<a href="javascript:;" class="quickbar_delete_link">'+ quickbarTexts['delete'] +'</a>';

				var moveCell = row.insertCell(-1);
				moveCell.innerHTML = '<div class="bqhandle"> </div>';
			}
		},
		save_quickbar: function() {
			var rows = __('dsplus_quickbar').rows;
			var newData = [];
			for (var i = 0, row; row = rows[i]; i++) {
				var num = row.id.replace('dsplus_quickbar_', '');
				var data;
				if (num.indexOf('new_') == 0) {
					num = num.replace('new_', '');
					data = Settings.quickbar_addedData[num];
				} else if (Settings.quickbar_editData[num]) {
					data = Settings.quickbar_editData[num];
				} else if (Data.quickbar.isLinebreak(num)) {
					data = 0;
				} else {
					data = {
						name: Data.quickbar.getName(num),
						title: Data.quickbar.getTitle(num),
						imgSrc: Data.quickbar.getImg(num),
						target: Data.quickbar.getTarget(num),
						newTab: Data.quickbar.isNewTab(num)
					};
				}
				newData.push(data);
			}
			Data.quickbar.clear();
			for (var i = 0, max = newData.length; i < max; i++) {
				var dat = newData[i];
				if (dat == 0) {
					Data.quickbar.setLinebreak(i);
				} else {
					Data.quickbar.setName(i, dat.name);
					Data.quickbar.setTitle(i, dat.title);
					Data.quickbar.setImg(i, dat.imgSrc);
					Data.quickbar.setTarget(i, dat.target);
					Data.quickbar.setNewTab(i, dat.newTab);
				}
			}
			Data.quickbar.save();
			return true;
		},

		// configure hotkeys
		make_userKeys: function(text, optionContainer) {
			var tabHTML = '';
			var tables = '';
			var hotkeysTexts = texts.settings.userKeys_texts;
			var first = true;
			for (var key in hotkeysTexts) {
				tabHTML += '<tr><td style="padding: 1px 2px;" class="'+ (first ? 'selected' : '') +'"><a id="dsplus_hotkeys_'+ key +'" href="javascript:;" class="hotkeysTabLink">'+ hotkeysTexts[key].title +'</a></td></tr>';

				tables += '<table id="dsplus_hotkeys_'+ key +'_tab" style="'+ (first ? '' : 'display: none;') +'">';
				for (var subKey in hotkeysTexts[key]) {
					if (subKey != 'title' && hotkeysTexts[key].hasOwnProperty(subKey)) {
						var keyCode = Hotkeys.userKeys.hasOwnProperty(key) && Hotkeys.userKeys[key][subKey] || Hotkeys.stdKeys[key][subKey];
						var keyCodeString = Hotkeys.char(keyCode);
						tables += '<tr><td>'+ hotkeysTexts[key][subKey] +':</td><td><input id="dsplus_hotkey_input_'+ key +'_'+ subKey +'" class="dsplus dsplus_hotkey_input" value="[ '+ keyCodeString +' ]" style="text-align: center;"></td></tr>';
					}
				}
				tables += '</table>';
				first = false;
			}
			var HTML = '<b>'+ text +':</b>' +
			'<div style="display: table-row;">' +
				'<div style="display: table-cell;"><table class="vis modemenu">'+ tabHTML +'</table></div>' +
				'<div id="dsplus_hotkeys_container" style="display: table-cell;">'+ tables +'</div>' +
			'</div>';

			optionContainer.innerHTML = HTML;

			var popup = !(screen == 'settings' && game_data.mode == 'settings');
			var links = _C(optionContainer, 'hotkeysTabLink');
			for (var i = 0, link; link = links[i]; i++) {
				link.addEventListener('click', switchHotkeysTab, true);
			}

			var inputs = _C(optionContainer, 'dsplus_hotkey_input');
			for (var i = 0, input; input = inputs[i]; i++) {
				input.addEventListener('focus', inputFocus, false);
				input.addEventListener('keydown', inputKeyDown, true);
				input.addEventListener('keyup', inputKeyUp, false);
				input.addEventListener('blur', inputBlur, false);
			}

			function switchHotkeysTab() {
				this.blur();
				var tables = _T(optionContainer, 'table');
				for (var i = 1, table; table = tables[i]; i++) {
					_hide(table);
				}
				_show(__(this.id +'_tab'));
				var selected = _C(optionContainer, 'selected', 0);
				if (selected)
					selected.classList.remove('selected');
				this.parentNode.classList.add('selected');
			}
			function inputFocus() {
				this.oldValue = this.value;
				this.value = '';
			}
			function inputKeyDown(e) {
				e = e || window.event;
				var keyCode = Hotkeys.getKeyCode(e);
				if (!Hotkeys.isSpecialKey(e) && Hotkeys.isAllowed(keyCode)) {
					this.value = Hotkeys.char(keyCode);
					this.keyCode = keyCode;
					e.cancelBubble = true;
					e.returnValue = false;

					if (e.stopPropagation) {
						e.stopPropagation();
						e.preventDefault();
					}
					// fix an issue with the "Esc"-Key
					var input = this;
					setTimeout(function() {
						input.value = Hotkeys.char(input.keyCode);
					}, 10);
				} else {
					if (this.value)
						this.oldValue = this.value;
					this.value = '';
				}
			}
			function inputKeyUp() {
				if (this.value) {
					if (/(\[ )+/.test(this.value)) {
						var match = this.value.match(/([^\[ ]+)/);
						this.value = match[1];
					}
					this.value = '[ '+ this.value +' ]';
				}
			}
			function inputBlur() {
				if (!this.value) {
					this.value = this.oldValue;
				}
				delete this.oldValue;
			}
		},
		save_userKeys: function() {
			var hotkey_inputs = _C(doc, 'dsplus_hotkey_input');
			for (var i = 0, input; input = hotkey_inputs[i]; i++) {
				if (input.keyCode) {
					var match = input.id.match(/dsplus_hotkey_input_([^_]+)_(.+)/);
					var key = match[1];
					var subKey = match[2];
					var keyCode = input.keyCode;
					Hotkeys.userKeys[key] = Hotkeys.userKeys[key] || {};
					Hotkeys.userKeys[key][subKey] = keyCode;
				}
			}
			Data.set('userHotkeys', Hotkeys.userKeys);
			Data.save();
			return true;
		}
	};


	var Storage = {
		_resTicker: [],
		_interval: 10,
		colorize: false,
		dsplus_wood: null,
		dsplus_stone: null,
		dsplus_iron: null,
		size: 0,
		attachResTicker: function(el, step, max) {
			var val = parseInt(el.innerHTML.replace(/(<[^>]*>)|\.|\s/g, ''), 10);
			step = parseFloat(step) / 3600;
			max = parseInt(max, 10);
			if (val < max)
				Storage._resTicker.push([el, val, step, max]);
		},
		tickRes: function() {
			for (var i = 0, ticker; ticker = Storage._resTicker[i]; i++) {
				var val = Math.min(ticker[1] + ticker[2] * Storage._interval, ticker[3]);
				ticker[1] = val;
				ticker[0].innerHTML = lib.formatNumber(Math.round(val), true, true);
				if (val == ticker[3]) {
					// reached limit: delete element
					Storage._resTicker.splice(i, 1);
					i--;
				}
			}
			Storage.colorRes();
		},
		colors: [],
		updateInfoRes: function() {
			var wood = parseInt(__('wood').innerHTML, 10);
			var woodColor = Storage.getColor(wood);
			Storage.dsplus_wood.innerHTML = lib.formatNumber(wood, true, true);
			Storage.dsplus_wood.style.backgroundColor = woodColor.toString();
			Storage.dsplus_wood.style.color = woodColor.getContrastColor().toString();

			var stone = parseInt(__('stone').innerHTML, 10);
			var stoneColor = Storage.getColor(stone);
			Storage.dsplus_stone.innerHTML = lib.formatNumber(stone, true, true);
			Storage.dsplus_stone.style.backgroundColor = stoneColor.toString();
			Storage.dsplus_stone.style.color = stoneColor.getContrastColor().toString();

			var iron = parseInt(__('iron').innerHTML, 10);
			var ironColor = Storage.getColor(iron);
			Storage.dsplus_iron.innerHTML = lib.formatNumber(iron, true, true);
			Storage.dsplus_iron.style.backgroundColor = ironColor.toString();
			Storage.dsplus_iron.style.color = ironColor.getContrastColor().toString();
		},
		initResTicker: function() {
			window.setInterval(Storage.tickRes, Storage._interval*1000);
		},
		getColor: function(num, storage) {
			if (isNaN(storage))
				storage = Storage.size;
			var idx = 100;
			if (storage) {
				var idx = Math.round(num / storage * 100);
				if (isNaN(idx) || idx < 0 || idx > 100)
					return new Color(0, 0, 0);
			}
			var color = Storage.colors[idx];
			return new Color(color[0], color[1], color[2]);
		},
		getColor_old: function() {
			if (arguments.length == 1 && !Storage.size || arguments.length != 1 && !arguments[1]) {
				return new Color('rgb('+ Storage.colors[100].toString() +')');
			} else {
				var idx = Math.round(arguments[0] / (arguments.length == 1 ? Storage.size : arguments[1]) * 100);
				if (isNaN(idx) || idx < 0 || idx > 100)
					return new Color(0, 0, 0);
				return new Color('rgb('+ Storage.colors[idx].toString() +')');
			}
		},
		colorInfoRes: function() {
			var wood =  __('wood');
			Storage.dsplus_wood = _ce('span', '', {
				id: 'dsplus_wood',
				'class': 'res',
				title: wood.title
			});
			_before(Storage.dsplus_wood, wood);
			_hide(wood);

			var stone = __('stone');
			Storage.dsplus_stone = _ce('span', '', {
				id: 'dsplus_stone',
				'class': 'res',
				title: stone.title
			});
			_before(Storage.dsplus_stone, stone);
			_hide(stone);

			var iron  = __('iron');
			Storage.dsplus_iron = _ce('span', '', {
				id: 'dsplus_iron',
				'class': 'res',
				title: iron.title
			});
			_before(Storage.dsplus_iron, iron);
			_hide(iron);

			Storage.size = parseInt(__('storage').innerHTML, 10);
			window.setInterval(Storage.updateInfoRes, 1000);
			Storage.updateInfoRes();
		},
		colorRes: function() {
			if (!Storage.colorize)
				return;

			var cells = document.getElementsByTagName('td');
			for (var i = 0, max = cells.length; i < max; i++) {
				if (!cells[i].classList.contains('wood'))
					continue;

				var woodEl = cells[i];

				var stoneEl = cells[++i];
				if (!stoneEl)
					continue;

				var ironEl = cells[++i];
				if (!ironEl)
					continue;

				var storageEl = cells[++i];
				var storage = parseInt(storageEl.innerHTML.replace(/(<[^>]*>)|\.|[^\d]/g, ''), 10);
				if (isNaN(storage))
					continue;

				var wood = parseInt(woodEl.innerHTML.replace(/(<[^>]*>)|\.|\s/g, ''), 10);
				var woodColor = Storage.getColor(wood, storage);
				woodEl.style.backgroundColor = woodColor.toString();
				woodEl.style.color  = woodColor.getContrastColor().toString();

				var stone = parseInt(stoneEl.innerHTML.replace(/(<[^>]*>)|\.|\s/g, ''), 10);
				var stoneColor = Storage.getColor(stone, storage);
				stoneEl.style.backgroundColor = stoneColor.toString();
				stoneEl.style.color = stoneColor.getContrastColor().toString();

				var iron = parseInt(ironEl.innerHTML.replace(/(<[^>]*>)|\.|\s/g, ''), 10);
				var ironColor = Storage.getColor(iron, storage);
				ironEl.style.backgroundColor = ironColor.toString();
				ironEl.style.color  = ironColor.getContrastColor().toString();
			}
		},

		init_prod: function() {
			var production_table = __('production_table');
			if (!production_table || !Data.settings.get('storageColor_enabledAt', 'production'))
				return;

			Storage.colorize = true;
			var idx = -1;
			var cells = __('production_table').rows[0].cells;
			for (var i = 0, max = cells.length; i < max && idx == -1; i++) {
				if (texts.regex.resources.test(cells[i].innerHTML))
					idx = i;
			}
			if (idx == -1)
				return;
			var woodHeaderCell = cells[idx];
			woodHeaderCell.innerHTML = '<span class="res wood">&nbsp;'+ texts.gui.wood +'</span>';
			var nextCell = _next(woodHeaderCell);

			var stoneHeaderCell = _ce('th', '<span class="res stone">&nbsp;'+ texts.gui.stone +'</span>');
			_before(stoneHeaderCell, nextCell);

			var ironHeaderCell = _ce('th', '<span class="res iron">&nbsp;'+ texts.gui.iron +'</span>');
			_before(ironHeaderCell, nextCell);

			var spans = _T(production_table, 'span');
			for (var i = 0, span; span = spans[i]; i++) {
				if (!span.classList.contains('wood'))
					continue;

				var woodEl  = span;
				var stoneEl = _next(woodEl);
				var ironEl  = _next(stoneEl);
				var woodCell = woodEl.parentNode;
				if (woodCell.tagName != 'TD')
					continue;
				var row = woodCell.parentNode;
				var nextCell = _next(woodCell);

				woodCell.classList.add('wood');
				woodCell.style.textAlign = 'right';

				var stoneCell = _ce('td', stoneEl.innerHTML, {
					'class': 'stone',
					'style': 'text-align: right;'
				});
				_before(stoneCell, nextCell);

				var ironCell = _ce('td', ironEl.innerHTML, {
					'class': 'iron',
					'style': 'text-align: right;'
				});
				_before(ironCell, nextCell);

				woodCell.innerHTML = woodEl.innerHTML;
				i -= 3;
			}
		},
		init_market_call: function() {
			if (!Data.settings.get('storageColor_enabledAt', 'market'))
				return;

			var wood_checkbox = __('checkbox_wood');
			if (!wood_checkbox)
				return;
			var woodHeaderCell = wood_checkbox.parentNode;
			woodHeaderCell.style.textAlign = 'center';
			woodHeaderCell.classList.add('nowrap');
			var resSpan = _ce('span', '&nbsp;', {
				'class': 'res wood'
			});
			var firstChild = woodHeaderCell.firstChild;
			if (firstChild.nodeType == 3) {
				_before(resSpan, firstChild);
				firstChild.nodeValue = firstChild.nodeValue.replace(/\s$/, '');
				resSpan.appendChild(firstChild);

				var stone_checkbox = __('checkbox_stone');
				var stoneHeaderCell = stone_checkbox.parentNode;
				stoneHeaderCell.style.textAlign = 'center';
				stoneHeaderCell.classList.add('nowrap');
				var resSpan = _ce('span', '&nbsp;', {
					'class': 'res stone'
				});
				var firstChild = stoneHeaderCell.firstChild;
				_before(resSpan, firstChild);
				firstChild.nodeValue = firstChild.nodeValue.replace(/\s$/, '');
				resSpan.appendChild(firstChild);

				var iron_checkbox = __('checkbox_iron');
				var ironHeaderCell = iron_checkbox.parentNode;
				ironHeaderCell.style.textAlign = 'center';
				ironHeaderCell.classList.add('nowrap');
				var resSpan = _ce('span', '&nbsp;', {
					'class': 'res iron'
				});
				var firstChild = ironHeaderCell.firstChild;
				_before(resSpan, firstChild);
				firstChild.nodeValue = firstChild.nodeValue.replace(/\s$/, '');
				resSpan.appendChild(firstChild);
			}

			var rows = __('village_list').rows;
			for (var i = 1, row; row = rows[i]; i++) {
				var cells = row.cells;
				var woodCell  = null;
				var stoneCell = null;
				var ironCell  = null;
				for (var j = 0, max = cells.length; j < max; j++) {
					if (cells[j].classList.contains('wood')) {
						woodCell  = cells[j];
						stoneCell = cells[j+1];
						ironCell  = cells[j+2];
						j = max;
					}
				}
				if (!woodCell || !stoneCell || !ironCell ||
					woodCell.children[0].childNodes.length > 2)
					continue;

				var woodSpan = woodCell.children[0].children[0];
				woodSpan.classList.remove('wood');
				woodSpan.classList.remove('warn_90');
				woodSpan.classList.remove('warn');
				woodSpan.classList.add('res');
				woodCell.appendChild(woodSpan);
				_remove(woodCell.children[0]);
				woodCell.style.textAlign = 'right';

				var stoneSpan = stoneCell.children[0].children[0];
				stoneSpan.classList.remove('stone');
				stoneSpan.classList.remove('warn_90');
				stoneSpan.classList.remove('warn');
				stoneSpan.classList.add('res');
				stoneCell.appendChild(stoneSpan);
				_remove(stoneCell.children[0]);
				stoneCell.style.textAlign = 'right';

				var ironSpan = ironCell.children[0].children[0];
				ironSpan.classList.remove('iron');
				ironSpan.classList.remove('warn_90');
				ironSpan.classList.remove('warn');
				ironSpan.classList.add('res');
				ironCell.appendChild(ironSpan);
				_remove(ironCell.children[0]);
				ironCell.style.textAlign = 'right';
			}
		},
		init_market_own: function() {
			if (!Data.settings.get('storageColor_enabledAt', 'market'))
				return;

			var rows = _T(__('inline_popup_content'), 'table', 'last').rows;
			for (var i = 0, row; row = rows[i]; i++) {
				var woodCell = row.cells[1];
				var children = woodCell.children;
				if (children.length != 6)
					continue;

				var woodImg = children[0];
				woodImg.classList.add('float_left');
				var woodSpan = children[1];
				woodSpan.classList.remove('warn_90');
				woodSpan.classList.remove('warn');
				woodSpan.classList.add('res');
				woodSpan.classList.add('float_right');
				woodSpan.style.marginTop = '2px';

				var stoneImg = children[2];
				stoneImg.classList.add('float_left');
				var stoneSpan = children[3];
				stoneSpan.classList.remove('warn_90');
				stoneSpan.classList.remove('warn');
				stoneSpan.classList.add('res');
				stoneSpan.classList.add('float_right');
				stoneSpan.style.marginTop = '2px';

				var ironImg = children[4];
				ironImg.classList.add('float_left');
				var ironSpan = children[5];
				ironSpan.classList.remove('warn_90');
				ironSpan.classList.remove('warn');
				ironSpan.classList.add('res');
				ironSpan.classList.add('float_right');
				ironSpan.style.marginTop = '2px';

				var nextCell = _next(woodCell);
				woodCell.classList.add('wood');

				var stoneCell = _ce('td', '', {
					'class': 'nowrap stone'
				});
				stoneCell.appendChild(stoneImg);
				stoneCell.appendChild(stoneSpan);
				_before(stoneCell, nextCell);

				var ironCell = _ce('td', '', {
					'class': 'nowrap iron'
				});
				ironCell.appendChild(ironImg);
				ironCell.appendChild(ironSpan);
				_before(ironCell, nextCell);
			}
		},
		init_coin_mint: function() {
			if (!Data.settings.get('storageColor_enabledAt', 'coin_mint'))
				return;

			var coin_overview_table = __('coin_overview_table');
			var rows = coin_overview_table.rows;
			if (rows[1].cells.length == 4) {
				var newColSpan = 5;
				rows[0].cells[0].colSpan = newColSpan;
				rows[rows.length-1].cells[0].colSpan = newColSpan;

				var woodHeaderCell = rows[1].cells[1];
				woodHeaderCell.innerHTML = '<span class="res wood">&nbsp;'+ texts.gui.wood +'</span>';
				var nextCell = rows[1].cells[2];

				var stoneHeaderCell = _ce('th', '<span class="res stone">&nbsp;'+ texts.gui.stone +'</span>');
				_before(stoneHeaderCell, nextCell);

				var ironHeaderCell = _ce('th', '<span class="res iron">&nbsp;'+ texts.gui.iron +'</span>');
				_before(ironHeaderCell, nextCell);
			}

			for (var i = 2, row; row = rows[i]; i++) {
				var resCell = row.cells[1];
				if (resCell.children.length < 3)
					continue;
				var nextCell = row.cells[2];
				var wood  = resCell.children[0].innerHTML;
				var stone = resCell.children[1].innerHTML;
				var iron  = resCell.children[2].innerHTML;
				// update the existing Cells
				if (row.cells.length > 4) {
					var woodCell  = row.cells[2];
					woodCell.innerHTML = wood;

					var stoneCell = row.cells[3];
					stoneCell.innerHTML = stone;

					var ironCell  = row.cells[4];
					ironCell.innerHTML = iron;
				}
				// add wood, stone and iron cells
				else {
					var woodCell = _ce('td', wood, {
						'class': 'nowrap wood',
						'style': 'text-align: right;'
					});
					_before(woodCell, nextCell);

					var stoneCell = _ce('td', stone, {
						'class': 'nowrap stone',
						'style': 'text-align: right;'
					});
					_before(stoneCell, nextCell);

					var ironCell = _ce('td', iron, {
						'class': 'nowrap iron',
						'style': 'text-align: right;'
					});
					_before(ironCell, nextCell);

					_hide(resCell);
				}
			}
		}
	};


	var Hotkeys = {
		textinputActive: false
		, stdKeys: {
			all: {
				abort: 27
				, nextVillage: 107
				, prevVillage: 109
				, main: 72
				, map: 75
				, place: 80
				, market: 77
			}
			, report: {
				abort: 27
				, leftArrow: 37
				, rightArrow: 39
				, 'delete': 46
				, startReading: 82
				, move: 86
			}
			, place: {
				'submit': 13
				, selectAll: 65
				, openFarmlist: 70
				, farmList1:  49
				, farmList2:  50
				, farmList3:  51
				, farmList4:  52
				, farmList5:  53
				, farmList6:  54
				, farmList7:  55
				, farmList8:  56
				, farmList9:  57
				, farmList10: 48
			}
			, market: {
				'submit': 13
			}
		}
		, userKeys: Data.get('userHotkeys')
		, actions: {
			all: {
				abort: function()
				{
					Bot.deactivateFarmbot();
				}
				// + (change village)
				, nextVillage: function()
				{
					var arrowRight = _C(doc, 'arrowRight', 0);
					if (!arrowRight)
						arrowRight = _C(doc, 'groupRight', 0);
					if (arrowRight)
					{
						arrowRight.focus();
						arrowRight.click();
					}
				}
				// - (change village)
				, prevVillage: function()
				{
					var arrowLeft = _C(doc, 'arrowLeft', 0);
					if (!arrowLeft)
						arrowLeft = _C(doc, 'groupLeft', 0);
					if (arrowLeft)
					{
						arrowLeft.focus();
						arrowLeft.click();
					}
				}
				, main: function()
				{
					location.href = game_data.link_base_pure +'main';
				}
				, map: function()
				{
					location.href = game_data.link_base_pure +'map';
				}
				, place: function()
				{
					location.href = game_data.link_base_pure +'place';
				}
				, market: function()
				{
					location.href = game_data.link_base_pure +'market';
				}
			}
			, report: {
				// 'Esc' to abbort the automatic reading
				abort: function()
				{
					Bot.deactivateReadMode();
				}
				// left arrow
				, leftArrow: function()
				{
					if (regex.viewId.test(location.href))
					{
						Report.next();
					}
					else
					{
						var prevEl = _prev(__('report_list').parentNode);
						if (prevEl && prevEl.tagName == 'TABLE')
						{
							var currentPageEl = _T(prevEl, 'strong', 'last');
							var prevPageLink = _prev(currentPageEl);
							prevPageLink.focus();
							prevPageLink.click();
						}
					}
				}
				// right arrow
				, rightArrow: function()
				{
					if (regex.viewId.test(location.href))
					{
						Report.prev();
					}
					else
					{
						var prevEl = _prev(__('report_list').parentNode);
						if (prevEl && prevEl.tagName == 'TABLE')
						{
							var currentPageEl = _T(prevEl, 'strong', 'last');
							var nextPageLink = _next(currentPageEl);
							nextPageLink.focus();
							nextPageLink.click();
						}
					}
				}
				// delete was pressed
				, 'delete': function()
				{
					Report['delete']();
				}
				// 'r' to start automatic reading
				, startReading: function()
				{
					Bot.startReportReading();
				}
				// 'v' to move
				, move: function()
				{
					if (/view=\d+/.test(location.href))
						Report.move();
				}
			}
			, place: {
				'submit': function(event, onsubmit)
				{
					if (__('target_attack'))
					{
						var xCoord = parseInt(__('inputx').value, 10);
						var yCoord = parseInt(__('inputy').value, 10);
						var filled = false;
						var unitInputs = _C(__('units_form'), 'unitsInput');
						for (var i = 0, max = unitInputs.length; i < max && !filled; i++)
						{
							var input = unitInputs[i];
							if (input.tagName == 'INPUT')
							{
								var val = parseInt(input.value, 10);
								if (val && !isNaN(val))
									filled = true;
							}
						}
						if (xCoord && !isNaN(xCoord) && yCoord && !isNaN(yCoord) && filled)
						{
							// passed through'onsubmit'
							if (onsubmit)
							{
								return true;
							}
							else
							{
								var target_attack = __('target_attack');
								target_attack.focus();
								target_attack.click();
							}
						}
						else
						{
							if (!xCoord || isNaN(xCoord) || !yCoord || isNaN(yCoord))
							{
								lib.alert(texts.gui.noCoords);
							}
							else if (!filled)
							{
								lib.alert(texts.gui.noUnitsSelected);
							}
							// passed through 'onsubmit'
							if (onsubmit)
							{
								return false;
							}
						}
					}
					else if (__('troop_confirm_go'))
					{
						var troop_confirm_go = __('troop_confirm_go');
						if (doc.activeElement != troop_confirm_go)
						{
							troop_confirm_go.focus();
							troop_confirm_go.click();
						}
					}
				}
				, selectAll: function()
				{
					__('selectAllUnits').click();
				}
				, openFarmlist: function()
				{
					__('dsplus_farmlistLink').click();
				}
				, farmList1:  function() { Farmlist.click(1); }
				, farmList2:  function() { Farmlist.click(2); }
				, farmList3:  function() { Farmlist.click(3); }
				, farmList4:  function() { Farmlist.click(4); }
				, farmList5:  function() { Farmlist.click(5); }
				, farmList6:  function() { Farmlist.click(6); }
				, farmList7:  function() { Farmlist.click(7); }
				, farmList8:  function() { Farmlist.click(8); }
				, farmList9:  function() { Farmlist.click(9); }
				, farmList10: function() { Farmlist.click(10); }
			}
			, market: {
				'submit': function(event, onsubmit)
				{
					if (__('inputx') || doc.querySelector('input[name="target_id"]'))
					{
						var submitBtn = doc.querySelector('input[type="submit"]');
						if (doc.activeElement != submitBtn)
						{
							submitBtn.focus();
							submitBtn.click();
						}
					}
				}
			}
		}
		, els: {
			all: {}
			, report: {
				leftArrow: function(ch, dynamicLoaded)
				{
					var links = _T(__('content_value'), 'a');
					for (var i = 0, link; link = links[i]; i++)
					{
						if (link.text.indexOf(texts.gui.reportNext) > -1)
						{
							link.classList.add('nowrap');
							link.innerHTML = link.innerHTML +' ['+ ch +']';
						}
					}
				}
				, rightArrow: function(ch, dynamicLoaded)
				{
					var links = _T(__('content_value'), 'a');
					for (var i = 0, link; link = links[i]; i++)
					{
						if (link.text.indexOf(texts.gui.reportPrev) > -1)
						{
							link.classList.add('nowrap');
							link.innerHTML = link.innerHTML +' ['+ ch +']';
						}
					}
				}
				, 'delete': function(ch, dynamicLoaded)
				{
					var links = _T(__('content_value'), 'a');
					for (var i = 0, link; link = links[i]; i++)
					{
						if (link.text.indexOf(texts.gui.reportDelete) > -1)
						{
							link.classList.add('nowrap');
							link.innerHTML = link.innerHTML +' ['+ ch +']';
						}
					}
				}
				, move: function(ch, dynamicLoaded)
				{
					var links = _T(__('content_value'), 'a');
					for (var i = 0, link; link = links[i]; i++)
					{
						if (link.text.indexOf(texts.gui.reportMove) > -1)
						{
							link.classList.add('nowrap');
							link.innerHTML = link.innerHTML +' ['+ ch +']';
						}
					}
				}
			}
			, place: {
				'submit': function(ch, dynamicLoaded)
				{
					if (dynamicLoaded)
						return;

					var el;
					if (__('target_attack'))
					{
						el = __('target_attack');
					}
					else if (__('troop_confirm_go'))
					{
						el = __('troop_confirm_go');
					}
					if (el)
					{
						el.value = el.value +' ['+ ch +']';
					}
				}
				, selectAll: function(ch, dynamicLoaded) {
					if (dynamicLoaded)
						return;

					var el = __('selectAllUnits');
					if (el)
					{
						el.textContent = el.textContent +' ['+ ch +']';
					}
				}
				, openFarmlist: function()
				{
					//
				}
			}
			, market: {
				'submit': function(ch, dynamicLoaded)
				{
					if (__('inputx') || document.querySelector('input[name=target_id]'))
					{
						var submitButton = document.querySelector('input[type="submit"]');
						submitButton.value = submitButton.value +' ['+ ch +']';
					}
				}
			}
		}
		, specialChars: {
			13: String.fromCharCode(8629)	// enter
			, 27: 'Esc'
			, 32: 'Space'
			, 37: String.fromCharCode(8592)	// arrow key left
			, 38: String.fromCharCode(8593)	// arrow key up
			, 39: String.fromCharCode(8594)	// arrow key right
			, 40: String.fromCharCode(8595)	// arrow key down
			, 45: 'Einfg'
			, 46: 'Entf'
			, 106: '*'	// numpad *
			, 107: '+'	// numpad +
			, 109: '-'	// numpad -
			, 110: ','	// numpad ,
			, 111: '/'	// numpad /
			, 163: '#'
			, 188: ','
			, 189: '-'
			, 190: '.'
		}
		, char: function(num)
		{
			if (this.specialChars.hasOwnProperty(num))
				return this.specialChars[num];
			return String.fromCharCode(num);
		}
		, getKeyCode: function(event)
		{
			var keyCode = event.keyCode;
			// key on numeric keypad was pressed
			if (keyCode >= 96 && keyCode <= 105)
				keyCode -= 48;
			return keyCode;
		}
		, isSpecialKey: function(event)
		{
			return /*event.shiftKey ||*/ event.ctrlKey || event.altKey;
		}
		, isAllowed: function(keyCode)
		{
			return (Hotkeys.specialChars[keyCode] || keyCode >= 48 && keyCode <= 57 || keyCode >= 65 && keyCode <= 90);
		}
		, last2KeyCodes: []
		, confirmationBox: false
		, keyDown: function(event)
		{
			CoordSelector.keyDown(event);
			var keyCode = Hotkeys.getKeyCode(event);
			if ((keyCode == 13 || keyCode == 27) && __('confirmation-box'))
			{
				Hotkeys.confirmationBox = true;
			}
			Hotkeys.last2KeyCodes.push(keyCode);
			Hotkeys.last2KeyCodes = Hotkeys.last2KeyCodes.slice(-2);
		}
		, keyUp: function(event)
		{
			CoordSelector.keyUp(event);
			if (Hotkeys.isSpecialKey(event) ||
				__('dsplus_settings') && __('dsplus_settings').style.display != 'none')
				return;

			var activeElement = document.activeElement;
			var type = activeElement.type;
			if (activeElement.nodeName == 'INPUT' && (
					type == 'text' ||
					type == 'password'
				) ||
				activeElement.nodeName == 'TEXTAREA')
				return;

			var key, keyPressed = false;
			var keyCode = Hotkeys.getKeyCode(event);
			// confirmation-box was open
			if (Hotkeys.confirmationBox)
			{
				Hotkeys.confirmationBox = false;
				if (keyCode == 13 || keyCode == 27)
					return;
			}
			// only key up no key down triggered (key was pressed on another page or something like that)
			if (keyCode != Hotkeys.last2KeyCodes[0] && keyCode != Hotkeys.last2KeyCodes[1])
				return;

			for (var name in Hotkeys.stdKeys.all)
			{
				key = Hotkeys.userKeys.all[name] || Hotkeys.stdKeys.all[name];
				if (key == keyCode)
				{
					Hotkeys.actions.all[name]();
					keyPressed = true;
					break;
				}
			}
			for (var name in Hotkeys.stdKeys[screen])
			{
				key = Hotkeys.userKeys.hasOwnProperty(screen) && Hotkeys.userKeys[screen][name] || Hotkeys.stdKeys[screen][name];
				if (key == keyCode)
				{
					Hotkeys.actions[screen][name](event);
					keyPressed = true;
					break;
				}
			}

			if (keyPressed)
			{
				event.cancelBubble = true;
				event.returnValue = false;

				if (event.stopPropagation)
				{
					event.stopPropagation();
					event.preventDefault();
				}
			}
			return false;
		}
	};

	var CoordSelector = {
		enabled: Data.settings.get('hotkeys_coordSelector')
		, ctrlDown: false
		, coordRange: false
		, curElement: null
		, addCoordSpans: function(node)
		{
			if (node.parentNode.tagName != 'TEXTAREA')
			{
				if (node.nodeType == 3 && node.nodeValue)
				{
					var res = node.nodeValue.match(/\d{1,3}\|\d{1,3}/g);
					var pos = 0;
					if (res && (res.length > 1 || node.parentNode.firstChild != node)) {
						var cp = node.nodeValue.indexOf(res[0]);
						var oldValue = node.nodeValue;
						node.nodeValue = node.nodeValue.substr(pos, cp);
						var span = ce('span');
						span.innerHTML = res[0];
						if (node.nextSibling)
						{
							node.parentNode.insertBefore( span, node.nextSibling );
						}
						else
						{
							node.parentNode.appendChild( span );
						}
						var rest = document.createTextNode(oldValue.substr(cp+res[0].length));
						if (span.nextSibling)
						{
							node.parentNode.insertBefore( rest, span.nextSibling );
						}
						else
						{
							node.parentNode.appendChild(rest);
						}
					}
				}
			}
			var child = node.firstChild;
			while (child != null)
			{
					addCoordSpans(child);
					child = child.nextSibling;
			}
		}
		, selectCoords: function(event)
		{
			if (CoordSelector.ctrlDown)
			{
				var el = document.elementFromPoint(event.pageX - document.documentElement.scrollLeft, event.pageY - document.documentElement.scrollTop);
				if (el != CoordSelector.curElement)
				{
					var sel = window.getSelection();
					if (CoordSelector.coordRange)
					{
						sel.removeAllRanges();
						CoordSelector.coordRange = false;
					}
					CoordSelector.curElement = el;
					el = el.firstChild;
					while (el && el.nodeType != 3)
						el = el.nextSibling;
					if (el && el.nodeValue)
					{
						var res = el.nodeValue.match(/(\d{1,3}\|\d{1,3})/g);
						if (res)
						{
							if (res.length == 1)
							{
								var idx = el.nodeValue.indexOf(res[0]);
								var range = document.createRange();
								range.setStart(el, idx);
								range.setEnd(el, idx + res[0].length);
								sel.removeAllRanges();
								sel.addRange(range);
								CoordSelector.coordRange = true;
							}
							else
								CoordSelector.addCoordSpans(curElement);
						}
					}
				}
			}
		}
		, keyDown: function(event)
		{
			if (!this.enabled)
				return;

			var keyCode = event.keyCode;
			if (keyCode == 17)
			{
				CoordSelector.ctrlDown = true;
			}
			else if (event.ctrlKey && (keyCode != 67 || keyCode == 88))
			{
				CoordSelector.ctrlDown = false;
			}
		}
		, keyUp: function(event)
		{
			if (!this.enabled)
				return;

			if (event.keyCode == 17)
			{
				CoordSelector.ctrlDown = false;
				if (CoordSelector.coordRange)
				{
					window.getSelection().removeAllRanges();
					CoordSelector.coordRange = false;
				}
			}
		}
	};


	var Village = {
		list: lib.storage.getValue('village_list', []),
		_levelPoints: function(key, level) {
			if (!level) return lib.buildingPoints[key];
			if (level == 1) return Math.round(lib.buildingPoints[key] * Math.pow(1.2, level)) - lib.buildingPoints[key];
			return Math.round(lib.buildingPoints[key] * Math.pow(1.2, level)) - Math.round(lib.buildingPoints[key] * Math.pow(1.2, level - 1));
		},
		own: lib.storage.getValue('ownVillages', []),
		THIS: game_data.village.id,
		// see Template.sum
		units: {},
		buildings: {},
		getBuildUps: function(ID, pointsDiff) {
			var ret = {};

			// simple build up
			for (var key in lib.buildingInfo) {
				// consider the conditions of the buildings!
				if (key == 'church_f' && Data.villages.getBuilding(ID, 'church') || key == 'church' && Data.villages.getBuilding(ID, 'church_f'))
					continue;	// already a church

				var level = Data.villages.getBuilding(ID, key);
				// TODO
				var points = Village._levelPoints(key, level);
				if (level == lib.buildingInfo[key].max_level)
					continue;	// reached max. level

				if (points <= pointsDiff) {
					ret[key] = ++level;
					var tmp = pointsDiff - points;
					for (var max = lib.buildingInfo[key].max_level; level < max && tmp > 0; level++) {
						// TODO
						points = Village._levelPoints(key, level);
						tmp -= points;
						if (tmp >= 0)
							ret[key] = level+1;
					}
				}
			}
			return ret;
		},
		getBuildUps2: function(ID, pointsDiff) {
			var counter;
			function getBuildUpsRec(buildings, pointsDiff) {
				var ret = {};
				counter++;
				if (counter >= 10000)	// to restrict the iteration and recursion
					return ret;
				for (var key in buildings) {
					if (lib.serverConfig.game.church && (key == 'church_f' && buildings.church || key == 'church' && buildings.church_f))
						continue;	// already a church

					var level = buildings[key];
					var points = Village._levelPoints(key, level);
					if (points == pointsDiff)
						ret[texts.building[key] + ' level ' + (level + 1)] = true;
					else if (points < pointsDiff) {
						var buildings2 = clone(buildings);
						buildings2[key]++;
						if (buildings2[key] == lib.buildingInfo[key].max_level)
							delete buildings2[key];
						var tmp = getBuildUpsRec(buildings2, pointsDiff - points);
						if (tmp != {})
							ret[texts.building[key] + ' level ' + (level + 1)] = tmp;
					}
					delete buildings[key];
				}
				return ret;
			}
			function dumpRet(obj, tabs) {
				var str = '';
				if (!tabs) {
					tabs = '';
					// Upgrading possibilities
					str = '\n'+ texts.gui.upgradingPossibilitiesConsole +':\n';
				}
				var ntabs = tabs + '-\t';
				for (var key in obj) {
					if (!obj.hasOwnProperty(key))
						continue;

					if (typeof(obj[key] ) == 'object') {
						str += '\n' + tabs + key;
						str += dumpRet(obj[key], ntabs);
					} else {
						str += '\n' + tabs + key;
					}
				}
				if (!tabs)
					lib.debug.log(str);
				return str;
			}
			function ret2Arr(obj) {
				for (var key in obj) {
					if (!obj.hasOwnProperty(key))
						continue;
					// TODO...
				}
			}

			// write not existing buildings in the object (level 0) and remove ready buildings
			var buildings = {};
			for (var build in lib.buildingInfo) {
				var level = Data.villages.getBuilding(ID, build);
				if (level < lib.buildingInfo[unit].max_level)
					buildings[build] = level;
			}
			counter = 0;
			var ret = getBuildUpsRec(buildings, pointsDiff);
			if (counter >= 10000)
				lib.alert(texts.gui.moreThanXPossibilities.replace('%number%', counter));
			dumpRet(ret);

			ret2Arr(ret);

			return ret;
		}
	};


	var Report = {
		els: {},
		containers: {},
		els_order: Data.get('report_els_order'),
		els_enabled: Data.get('report_els_enabled'),

		isPublic: false,
		ID: 0,
		isAttack: false,
		isSpecialEvent: false,
		isSpyReport: false,
		isConquered: false,
		returnedUnits: false,
		seenDefendingUnits: false,
		dot: '',
		reportName: '',
		time: 0,

		targetName: 0,
		targetCoords: '0|0',
		attackerID: 0,
		destinationID: 0,
		defenderID: 0,
		targetID: 0,

		attackerUnits: {
			num: {},
			loss: {},
			numInfo: true,
			lossInfo: true
		},
		defenderUnits: {
			num: {},
			loss: {},
			out: {},	// for units outside
			numInfo: true,
			lossInfo: true,
			outInfo: false
		},
		resources: {},
		buildings: {},
		result: {
			total: 0,
			max: 0,
			wood: 0,
			stone: 0,
			iron: 0,
			damage: [],
			loyalty: 100
		},

		analyse: function() {
			var match = location.href.match(regex.viewId);
			if (!this.isPublic && !match) {
				return;
			} else if (match) {
				// save it as number to compare (the greater, the newer)
				this.ID = parseInt(match[1], 10);
			}

			if (!this.els.attacker || !this.els.defender)
				return;
			this.isAttack = true;

			var spyRows = this.els.spy && this.els.spy.rows || [];
			var buildingsRowIdx = 0;
			if (spyRows.length > 1) {
				buildingsRowIdx = 1;
				if (_C(spyRows[1], 'farm_icon').length)
					buildingsRowIdx++;

				if (spyRows.length > buildingsRowIdx) {
					// special-event
					if (_T(spyRows[buildingsRowIdx], 'img').length && _T(spyRows[buildingsRowIdx], 'img', 0).src.indexOf('/events/') > -1) {
						buildingsRowIdx++;
						this.isSpecialEvent = true;
					}
				}

				if (spyRows.length <= buildingsRowIdx)
					buildingsRowIdx = 0;
			}

			// attack report
			var attackerLink = _T(this.els.attacker.rows[0], 'a', 0);
			var attackerLinkVal = attackerLink && attackerLink.href;
			if (attackerLinkVal) {
				this.attackerID = parseInt(attackerLinkVal.match(regex.id)[1], 10) || 0;
			}
			var destinationLink = _T(this.els.attacker.rows[1], 'a', 0);
			var destinationLinkVal = destinationLink && destinationLink.href;
			if (destinationLinkVal) {
				this.destinationID = parseInt(destinationLinkVal.match(regex.id)[1], 10) || 0;
			}

			var defenderLink = _T(this.els.defender.rows[0], 'a', 0);
			var defenderLinkVal = defenderLink && defenderLink.href;
			if (defenderLinkVal) {
				this.defenderID = parseInt(defenderLinkVal.match(regex.id)[1], 10) || 0;
			}
			var targetLink = _T(this.els.defender.rows[1], 'a', 0);
			var targetLinkVal = targetLink && targetLink.href;
			if (targetLinkVal) {
				this.targetID = parseInt(targetLinkVal.match(regex.id)[1], 10) || 0;
			}

			var match = targetLink && targetLink.textContent.match(texts.regex.villageName);
			if (!match)
				return;
			this.targetName = match[1];
			this.targetCoords = match[2] +'|'+ match[3];

			this.reportName = this.els.name.textContent.trim();
			if (this.els.dot)
				this.dot = this.els.dot.src.replace(/.*dots\//, '').replace(/\.png.*/, '');
			this.time = texts.locale.timeStr2Sec(this.els.time.textContent);

			// attackerUnits:
			var unitsTable = __('attack_info_att_units');
			if (unitsTable) {
				var rows = unitsTable.rows;
				var imgCells = rows[0].cells;
				var numCells = rows[1].cells;
				var lossCells = rows[2].cells;
				for (var i = 1, max = imgCells.length; i < max; i++) {
					var match = imgCells[i].children[0].src.match(regex.unitKey);
					var unitKey = match && match[1];
					if (unitKey) {
						if (numCells[i]) {
							var unitNum = parseInt(numCells[i].textContent, 10);
							if (unitNum)
								this.attackerUnits.num[unitKey] = unitNum;
						} else {
							this.attackerUnits.numInfo = false;
						}
						if (lossCells[i]) {
							var unitLoss = parseInt(lossCells[i].textContent, 10);
							if (unitLoss)
								this.attackerUnits.loss[unitKey] = unitLoss;
						} else {
							this.attackerUnits.lossInfo = false;
						}
						if (typeof(unitNum) != 'undefined' && typeof(unitLoss) != 'undefined') {
							var unitAlive = unitNum - unitLoss;
							if (unitAlive)
								this.returnedUnits = true;
						}
					}
				}
			}

			// defenderUnits:
			var unitsTable = __('attack_info_def_units');
			if (unitsTable) {
				this.seenDefendingUnits = true;
				if (unitsTable.rows.length == 2)
					this.isSpecialEvent = true;

				if (this.isSpecialEvent) {
					var cells = unitsTable.rows[0].cells;
					var numImg = cells[cells.length-1].children[0];
					var cells = unitsTable.rows[1].cells;
					var lossImg = cells[cells.length-1].children[0];
					while (numImg && lossImg) {
						var match = numImg.src.match(regex.unitKey);
						var unitKey = match && match[1];
						if (unitKey) {
							var unitNum  = parseInt( numImg.nextSibling.textContent.replace(/,/g, ''), 10);
							if (unitNum)
								this.defenderUnits.num[unitKey] = unitNum;
							var unitLoss = parseInt(lossImg.nextSibling.textContent.replace(/,/g, ''), 10);
							if (unitLoss)
								this.defenderUnits.loss[unitKey] = unitLoss;
						}
						numImg = _next(numImg);
						lossImg = _next(lossImg);
					}
				} else {
					var rows = unitsTable.rows;
					var imgCells = rows[0].cells;
					var numCells = rows[1].cells;
					var lossCells = rows[2].cells;
					for (var i = 1, max = imgCells.length; i < max; i++) {
						var match = imgCells[i].children[0].src.match(regex.unitKey);
						var unitKey = match && match[1];
						if (unitKey) {
							if (numCells[i]) {
								var unitNum = parseInt(numCells[i].textContent, 10);
								if (unitNum)
									this.defenderUnits.num[unitKey] = unitNum;
							} else {
								this.defenderUnits.numInfo = false;
							}
							if (lossCells[i]) {
								var unitLoss = parseInt(lossCells[i].textContent, 10);
								if (unitLoss)
									this.defenderUnits.loss[unitKey] = unitLoss;
							} else {
								this.defenderUnits.lossInfo = false;
							}
						}
					}
				}
			}

			// units outside:
			if (this.els.units_out) {
				var unitsTable = this.els.units_out.rows[1].cells[0].children[0];
				var rows = unitsTable.rows;
				var imgCells = rows[0].cells;
				var numCells = rows[1].cells;
				for (var i = 0; i < imgCells.length; i++) {
					var match = imgCells[i].innerHTML.match(regex.unitKey);
					var unitKey = match && match[1];
					if (unitKey) {
						var unitNum = parseInt(numCells[i].textContent, 10);
						if (unitNum)
							this.defenderUnits.out[unitKey] = unitNum;
					}
				}
				this.defenderUnits.outInfo = true;
			}

			// espionage
			if (this.els.spy) {
				// espied resources
				var spyResRow = spyRows[0];
				if (spyResRow && !_C(spyResRow, 'farm_icon').length) {
					this.isSpyReport = true;

					// in this row is a th and a td
					var spyResHTML = spyResRow.cells[1].innerHTML.replace(/<span class="grey">\.<\/span>/g, '');
					match = spyResHTML.match(regex.wood);
					if (match) this.resources.wood = parseInt(match[1], 10);

					match = spyResHTML.match(regex.stone);
					if (match) this.resources.stone = parseInt(match[1], 10);

					match = spyResHTML.match(regex.iron);
					if (match) this.resources.iron = parseInt(match[1], 10);
				}

				if (buildingsRowIdx) {
					// espied buildings
					var buildingLevels = new RegExp(texts.regex.buildingLevel.source, 'g');
					var buildingRowContent = spyRows[buildingsRowIdx].cells[1].textContent;
					var matches = buildingRowContent.match(buildingLevels) || [];
					var buildMatches = {};
					for (var i = 0; i < matches.length; i++) {
						var match = matches[i].trim().match(texts.regex.buildingLevel);
						// insert an attribute in matches with the name of the building and the level as the value
						var build = match[1];
						buildMatches[build] = parseInt(match[2], 10);
					}
					for (var build in lib.buildingInfo) {
						var level = 0;
						if (buildMatches[texts.building[build]]) {
							level = buildMatches[texts.building[build]];
						} else if (buildMatches[texts.buildings[build]]) {
							level = buildMatches[texts.buildings[build]];
						}
						if (level) this.buildings[build] = level;
					}
				}
			}

			// attack result
			if (this.els.result) {
				var resultRows = this.els.result.rows;
				var bootyRowCells = resultRows[0].cells;
				var booty = (bootyRowCells.length == 3);
				if (booty) {
					var bootyResult = bootyRowCells[1].innerHTML.replace(/<span class="grey">\.<\/span>/g, '');
					var bootyGes = bootyRowCells[2].innerHTML.replace(/<[^>]*>|\./g, '').split('/');
					this.result.total = parseInt(bootyGes[0], 10);
					this.result.max = parseInt(bootyGes[1], 10);

					// wood
					var match = bootyResult.match(regex.wood);
					if (match) this.result.wood = parseInt(match[1], 10);
					// stone
					var match = bootyResult.match(regex.stone);
					if (match) this.result.stone = parseInt(match[1], 10);
					// iron
					var match = bootyResult.match(regex.iron);
					if (match) this.result.iron = parseInt(match[1], 10);
				}

				var start = booty ? 1 : 0;
				if (resultRows.length > start) {
					for (var i = start, row; row = resultRows[i]; i++) {
						var match_damage = resultRows[i].textContent.match(texts.regex.damage);
						var match_loyalty = resultRows[i].textContent.match(texts.regex.loyaltyChange);
						if (match_damage) {
							var buildName = match_damage[1].trim();
							for (var key in lib.buildingInfo) {
								if (texts.building[key] == buildName || texts.buildings[key] == buildName) {
									this.result.damage[key] = parseInt(match_damage[2], 10);
								}
							}
						} else if (match_loyalty) {
							this.result.loyalty = parseInt(match_loyalty[1], 10);
							// conquest
							if (this.result.loyalty <= 0) {
								this.isConquered = true;
								this.result.loyalty = 25;
							}
						}
					}
				}
			}
		},

		status: 0,
		read: function() {
			if (!this.ID)
				return;

			// attack report
			if (this.isAttack && this.targetName) {
				var targetOwnerID = this.defenderID;
				if (this.isConquered) targetOwnerID = this.attackerID;

				Data.reports.setReportName(this.ID, this.reportName);
				Data.reports.setTime(this.ID, this.time);
				Data.reports.setDot(this.ID, this.dot);

				Data.villages.setName(this.targetID, this.targetName);
				Data.villages.setCoords(this.targetID, this.targetCoords);
				Data.villages.setOwnerID(this.targetID, targetOwnerID);

				if (pID == targetOwnerID) {
					// attack on an own village or a conquer
					Data.villages.setLocked(this.targetID, true);
				}

				if (this.result.max) {
					// there is some loot
					Data.reports.setLoot(this.ID, this.result.total, this.result.max);
				}

				var spyReportID = Data.villages.getSpyReportID(this.targetID);
				var lastReportID = Data.villages.getLastReportID(this.targetID);
				var newReport = this.ID > lastReportID;
				var newSpyReport = false;
				if (this.isSpyReport) {
					newSpyReport = this.ID > spyReportID;
					Data.reports.setTypeSpy(this.ID);
				} else {
					Data.reports.setTypeAttack(this.ID);
				}

				if (newReport || newSpyReport) {
					// new report

					if (pID == this.attackerID && !this.isConquered) {
						// attack from an own village -> add to own booty
						var timeKey = lib.formatTime2Key(lib.getServerTime(this.time * 1000));
						Data.booty.add(timeKey, {
							wood: this.result.wood,
							stone: this.result.stone,
							iron: this.result.iron
						});
						Data.booty.save();
					}

					var loyalty = this.result.loyalty;
					if (!newReport && loyalty != 100) {
						// reading a new report of a snob attack with espionage, that is older than the last report...
						var loyaltyNow = Data.villages.getMood(this.targetID, this.time);
						if (loyalty < loyaltyNow) {
							// last snob attack is longer than last attack ago: recalc the loyalty
							loyalty = Data.villages.getMood(this.targetID, Data.villages.getLastReportTime(this.targetID), this.time, loyalty);

							// save the loyalty
							Data.villages.setMood(this.targetID, loyalty);
						}
					} else if (newReport) {
						if (loyalty == 100 && Data.villages.exist(this.targetID)) {
							// in case that the loyalty is still under 100
							loyalty = Data.villages.getMood(this.targetID, this.time);
						}
						// save the loyalty
						Data.villages.setMood(this.targetID, loyalty);
					}

					// to calculate the EQ later
					var resExpected = Data.villages.getRes(this.targetID, this.time, false);
					var compareTime;
					if (this.isSpyReport) {
						compareTime = Data.villages.getSpyReportTime(this.targetID);
					} else {
						compareTime = Data.villages.getLastReportTime(this.targetID);
					}
					var isYounger = this.time > compareTime;

					if (newSpyReport) {
						if (spyReportID) Data.reports.setOld(spyReportID);
						Data.villages.setSpyReportID(this.targetID, this.ID);
						Data.villages.setSpyReportTime(this.targetID, this.time);
					}
					if (newReport) {
						if (lastReportID && (this.isSpyReport || lastReportID != spyReportID)) Data.reports.setOld(lastReportID);
						Data.villages.setLastReportID(this.targetID, this.ID);
						Data.villages.setLastReportTime(this.targetID, this.time);
					}

					// calculating the new EQ:
					var resources = {
						wood:  this.resources.wood  || 0,
						stone: this.resources.stone || 0,
						iron:  this.resources.iron  || 0
					};
					if (this.isConquered) {
						resources = resExpected;
					} else if (isYounger && (this.isSpyReport || this.result.total < this.result.max)) {
						Data.villages.recalcEQ(this.targetID, resExpected, this.result.total);
					} else if (!this.isSpyReport && this.returnedUnits) {
						resources.wood  = Math.max(resExpected.wood  - this.result.wood,  0);
						resources.stone = Math.max(resExpected.stone - this.result.stone, 0);
						resources.iron  = Math.max(resExpected.iron  - this.result.iron,  0);
					}
					Data.villages.setWood(this.targetID,  resources.wood);
					Data.villages.setStone(this.targetID, resources.stone);
					Data.villages.setIron(this.targetID,  resources.iron);

					// save the buildings
					var espiedBuildings = Object.keys(this.buildings).length;
					for (var build in lib.buildingInfo) {
						if (espiedBuildings) {
							var level = this.buildings[build] || 0;
							Data.villages.setBuilding(this.targetID, build, level);
						} else {
							// buildings (damaged by rams and catapults)
							var damageLevel = this.result.damage[build];
							if (typeof(damageLevel) != 'undefined')
								Data.villages.setBuilding(this.targetID, build, damageLevel);
						}
					}
					if (!espiedBuildings && this.isConquered) {
						Data.villages.setBuilding(this.targetID, 'statue', 0);
						Data.villages.setBuilding(this.targetID, 'church', 0);
						Data.villages.setBuilding(this.targetID, 'church_f', 0);
					}

					// save the units
					if (this.isConquered) {
						Data.villages.clearUnits(this.targetID);
						for (var unit in this.attackerUnits.num) {
							if (unit == 'snob')
								continue;
							var num = this.attackerUnits.num[unit];
							var loss =  this.attackerUnits.loss[unit] || 0;
							var alive = num - loss;
							Data.villages.setUnit(this.targetID, unit, alive);
						}
					} else if (this.seenDefendingUnits) {
						Data.villages.clearUnits(this.targetID);
						for (var unit in this.defenderUnits.num) {
							var num = this.defenderUnits.num[unit];
							var loss =  this.defenderUnits.loss[unit] || 0;
							var alive = num - loss;
							Data.villages.setUnit(this.targetID, unit, alive);
						}
					} else if (!this.isSpecialEvent) {
						Data.villages.clearUnits(this.targetID);
						for (var unit in lib.unitInfo)
							Data.villages.setUnit(this.targetID, unit, -1);
					}

					this.status = 1;
				} else if (this.ID == lastReportID || this.isSpyReport && this.ID == spyReportID) {
					// report is up to date
					this.status = 2;
				} else {
					// report is outdated
					Data.reports.setOld(this.ID);
					this.status = 3;
				}

				Data.villages.save();
			} else {
				// it is a non-attack-report (e.g. trading, awards etc.)
				Data.reports.setTypeNormal(this.ID);
			}

			Data.reports.save();
		},
		addSurvivorRow: function() {
			if (this.isSpecialEvent)
				return;

			var unitTables = {
				attackerUnits: __('attack_info_att_units'),
				defenderUnits: __('attack_info_def_units')
			};
			for (var key in unitTables) {
				if (!unitTables[key] || !this[key].numInfo || !this[key].lossInfo)
					continue;

				var rows = unitTables[key].rows;
				var survivorRow = rows[rows.length-1].cloneNode(true);
				if (!survivorRow.cells.length)
					continue;
				survivorRow.cells[0].innerHTML = texts.gui.survivor +':';

				for (var i = 0, max = rows[0].cells.length; i < max; i++) {
					var imgCell = rows[0].cells[i];
					var img = _T(imgCell, 'img', 0);
					if (!img)
						continue;
					var unit = img.src.match(regex.unitKey)[1];
					var survivorCell = survivorRow.cells[i];
					if (!survivorCell)
						continue;
					var survivor = this[key].num[unit] || 0;
					survivor -= this[key].loss[unit] || 0;
					if (survivor) survivorCell.classList.remove('hidden');
					else survivorCell.classList.add('hidden');
					survivorCell.innerHTML = survivor;
				}
				rows[0].parentNode.appendChild(survivorRow);
			}
		},
		addBashPointRow: function() {
			var unitTables = {
				attackerUnits: __('attack_info_att_units'),
				defenderUnits: __('attack_info_def_units')
			};
			var bashPoints = {
				attackerUnits: 0,
				defenderUnits: 0
			};
			for (var unit in this.attackerUnits.loss) {
				bashPoints.defenderUnits += this.attackerUnits.loss[unit] * (lib.bashPoints[unit][1] || 0);
			}
			for (var unit in this.defenderUnits.loss) {
				bashPoints.attackerUnits += this.defenderUnits.loss[unit] * (lib.bashPoints[unit][0] || 0);
			}

			var otherKey = {
				attackerUnits: 'defenderUnits',
				defenderUnits: 'attackerUnits'
			};
			for (var key in unitTables) {
				if (!unitTables[key] || !this[otherKey[key]].lossInfo)
					continue;

				var bashPointRow = unitTables[key].insertRow(-1);
				bashPointRow.insertCell(-1).textContent = texts.gui.bashPoints +':';
				var firstRowCells = unitTables[key].rows[0].cells;
				var colSpanCount = firstRowCells.length-1;
				var bashPointCell = bashPointRow.insertCell(-1);
				bashPointCell.colSpan = colSpanCount;
				bashPointCell.textContent = bashPoints[key];
			}
		},

		'delete': function() {
			if (screen == 'report' && /view=\d+/.test(location.href)) {
				if (Report.status == 3 || !Data.settings.get('confirm_deleting'))
				{
					deleteIt();
				}
				else if (Data.settings.get('confirm_deleting'))
				{
					var buttons = [{
						text: texts.gui.OK
						, callback: deleteIt
						, confirm: true
					}];
					UI.ConfirmationBox(texts.gui.confirmDeleteReport, buttons);
				}
			}

			function deleteIt()
			{
				var deleteReportLink = findLinkContains(texts.gui.reportDelete);
				if (deleteReportLink)
				{
					deleteReportLink.click();
				}
			}
		},
		move: function() {
			if (screen == 'report') {
				var moveReportLink = findLinkContains(texts.gui.reportMove);
				if (moveReportLink) {
					moveReportLink.click();
				}
			}
		},
		next: function(min, max) {
			if (screen == 'report') {
				Bot.deactivateReadMode();
				var nextReportLink = findLinkContains(texts.gui.reportNext);
				if (nextReportLink) {
					setRandom(function() {
						nextReportLink.click();
					}, (min || 0), (max || 0));
					return true;
				}
				return false;
			}
		},
		prev: function() {
			if (screen == 'report') {
				var prevReportLink = findLinkContains(texts.gui.reportPrev);
				if (prevReportLink) {
					prevReportLink.click();
					return true;
				}
				return false;
			}
		}
	};


	var Bot = (function () {
		var reportMode = Data.bot.get('reportMode');
		var continuesReading = Data.bot.get('continuesReading');
		var farmMode = Data.bot.get('farmMode');
		var start = false;
		var referenceID = Data.bot.get('referenceID');
		var limits = Data.bot.get('limits');
		var currentPage = 1;
		var maxPageNum = 1;
		var pagelistCell = null;
		var bodyHTML = doc.body.innerHTML;
		var active_protection = checkProtection();
		var paused = false;
		var overviewLink;

		// Autofarming
		if (Data.settings.get('bot_enabled')) {
			var buttonPlayPause = '';

			Style.add('bot').set(
				'#dsplus_autoFarm_button { font-size:12px; font-weight:bold; padding:5px 10px; border:0; background-color:#f4e4bc; color:#008000; }' +
				'#dsplus_autoFarm_button.pause { color:#ee0000; }' +
				'#dsplus_autoFarm_button:hover { color:#00aa00; }' +
				'#dsplus_autoFarm_button.pause:hover { color:#ff5555; }' +
				'#dsplus_autoFarm_button div { background:transparent url('+ buttonPlayPause +') no-repeat; display:block; float:'+ Style.left +'; margin-right:4px; position:relative; top:1px; height:11px; width:11px; }' +
				'#dsplus_autoFarm_button.pause div { background-position:-11px; }'
			);

			// add menu-item
			var title = farmMode ? texts.gui.autoFarm.stop : texts.gui.autoFarm.start;
			var cls = farmMode ? 'pause' : '';
			var cells = __('menu_row').cells;
			overviewLink = cells[1].children[0];
			var overviewLinkHref = overviewLink && overviewLink.href;
			var newCell = _ce(
				'td',
				'<a id="dsplus_autoFarm_button" class="'+ cls +'" href="'+ overviewLinkHref +'">' +
					'<div></div>' +
					'<span title="'+ title +'">'+ texts.gui.autoFarm.title +'</span>' +
				'</a>',
				{
					'class': 'menu-item'
				}
			);
			_before(newCell, cells[cells.length-1]);

			var autoFarm_button = __('dsplus_autoFarm_button');
			if (lib.isFirefox) {
				autoFarm_button.addEventListener('mouseenter', function() {
					pause();
				}, false);
				autoFarm_button.addEventListener('mouseleave', function() {
					resume();
				}, false);
			} else {
				autoFarm_button.addEventListener('mouseover', function(e) {
					pause();
				}, false);
				autoFarm_button.addEventListener('mouseout', function(e) {
					resume();
				}, false);
			}
			var prevent = 0;
			autoFarm_button.addEventListener('mouseup', function(e) {
				if (!e || e.which == 3)
					return;

				var newTab = (e.which == 2 || e.ctrlKey);
				if (farmMode || !newTab) {
					e.preventDefault();
					prevent = true;
				}
				toggleFarmbot(newTab);
			}, true);
			autoFarm_button.addEventListener('click', function(e) {
				if (prevent)
					e.preventDefault();
				prevent = false;
			}, true);
		}

		// stop all actions if the bot protection is active
		var reportOverview = false;
		if (active_protection) {
			var checkInterval = setInterval(function() {
				if (!checkProtection()) {
					clearInterval(checkInterval);
					run();
				}
			}, 1000);
		} else {
			run();
		}

		function run() {
			if (screen == 'report') {
				reportOverview = !regex.viewId.test(location.href);
				if (reportMode == 1 && reportOverview) {
					findUnknownReports();
				} else if (reportMode == 2 && reportOverview) {
					findUnreadReports();
				} else if (reportMode == 3 && !reportOverview) {
					clickNextReport();
				} else if (reportMode == 4 && reportOverview) {
					deleteOldReports();
				}
			}

			// Bot
			// ignore all other pages
			var applyFarmbot = {
				report: 1,
				overview_villages: 1,
				overview: 1,
				place: 1,
				map: 1,
				info_village: 1,
				am_farm: 1
			};
			if (Data.settings.get('bot_enabled') && farmMode && applyFarmbot[screen]) {
				farmbot();
			}
		}

		// Functions
		function checkProtection() {
			if (__('bot_check_image')) {
				active_protection = (__('bot_check_image').style.display != 'none');
			} else {
				active_protection = (
					texts.regex.botProtect.test(bodyHTML) ||
					regex.captcha.test(bodyHTML)
				);
			}

			return active_protection;
		}
		function changeReportMode(newMode) {
			reportMode = newMode;
			Data.bot.set('reportMode', reportMode);
			Data.bot.save();
		}
		function saveLimits() {
			Data.bot.set('limits', limits);
			Data.bot.save();
		}
		function getPagelistCell() {
			if (!pagelistCell) {
				var prevEl = _prev(__('report_list').parentNode);
				if (prevEl && prevEl.tagName == 'TABLE') {
					// there are more than one page
					var strongs = _T(prevEl, 'strong');
					if (!strongs.length || lib.hasPA && strongs.length < 2)
						return null;

					var currentPageEl = strongs[strongs.length-1];
					currentPage = parseInt(currentPageEl.textContent.replace(/[^\d]/g, ''), 10);

					pagelistCell = currentPageEl.parentNode;

					var lastPagelink = _T(pagelistCell, 'a', 'last');
					if (lastPagelink)
						maxPageNum = parseInt(lastPagelink.text.replace(/[^\d]/g, ''), 10);
					maxPageNum = Math.max(currentPage, maxPageNum);
				}
			}
			return pagelistCell;
		}
		function clickReportLink(linkEl) {
			setRandom(function() {
				linkEl.click();
			}, 500, 1000);
		}
		function changePage() {
			var targetPage = Math.round(limits[0] + (limits[1] - limits[0]) / 2);
			var pagelistCell = getPagelistCell();
			if (!targetPage || !pagelistCell) {
				// turn read-bot off
				deactivateReadMode(true);
				return;
			}
			var children = pagelistCell.children;
			var pagelink, pagehref;
			for (var i = 0, max = children.length; i < max && !pagelink; i++) {
				var child = children[i];
				var num = parseInt(child.innerHTML.replace(/[^\d]/g, ''), 10);
				if (num == targetPage) {
					pagelink = child;
				}
			}
			if (!pagelink) {
				var pageSelect = _T(pagelistCell, 'select', 0);
				if (!pageSelect) {
					deactivateReadMode(true);
					return;
				}
				var children = pageSelect.children;
				for (var i = 0, max = children.length; i < max && !pagehref; i++) {
					var child = children[i];
					var num = parseInt(child.innerHTML.replace(/[^\d]/g, ''), 10);
					if (num == targetPage) {
						pagehref = child.value;
					}
				}
			}
			setRandom(function() {
				if (pagelink) {
					pagelink.click();
				} else if (pagehref) {
					location.href = pagehref;
				}
			}, 500, 1200);
		}
		function changeFarmMode(newMode) {
			farmMode = newMode;
			Data.bot.set('farmMode', farmMode);
			Data.bot.save();
		}
		function waitBeforeRestart() {
			var randomTimeout = setRandom(function() {
				if (screen == 'place') {
					changeFarmMode(3);
				} else {
					changeFarmMode(1);
				}
				farmbot();
			}, 300000, 1200000);

		}
		function farm_manager_needed() {
			lib.error('Not possible! Please activate the Farm Assistant first...');
		}
		function nextVillage() {
			if (lib.hasPA && screen != 'place') {
				changeFarmMode(2);
				setLocation(game_data.link_base_pure.replace('screen=', 'screen=place'), 300, 600);
			} else if (!lib.hasPA && screen != 'overview_villages') {
				changeFarmMode(2);
				setLocation(game_data.link_base_pure.replace('screen=', 'screen=overview_villages'), 400, 800);
			} else {
				changeFarmMode(-1);
				var newHref = game_data.link_base_pure.replace('screen=', 'screen='+ screen);
				// next village
				var ownVills = Data.bot.get('ownVillages');
				var thisId = game_data.village.id;
				var nextId = thisId;
				if (ownVills.length > 1) {
					for (var i = 0, max = ownVills.length; i < max; i++) {
						if (ownVills[i] == thisId) {
							if (i == max-1) {
								nextId = ownVills[0];
							} else {
								nextId = ownVills[i+1];
							}
						}
					}
				}
				if (nextId == thisId) {
					// wait and repeat all again...
					waitBeforeRestart();
				} else {
					newHref = newHref.replace(/([?&])village=\d+/, '$1village='+ nextId);
					setLocation(newHref, 500, 900);
				}
			}
		}
		function dynamicLoad() {
			if (screen == 'place')
				farmbot();
		}
		function farmbot() {
			if (reportMode)
				return;

			var farmOver = Data.settings.get('bot_farmOver');
			if (farmOver == 2 && !game_data.player.farm_manager)
				return farm_manager_needed();

			var newReport = __('new_report');
			// read the new report(s)
			if (newReport.style.display != 'none' && farmMode < 4) {
				if (screen == 'report' && !regex.viewId.test(location.href)) {
					setRandom(startReportReading, 300, 600);
				} else {
					setLocation(game_data.link_base_pure.replace('screen=', 'screen=report'), 800, 1200);
				}
				return;
			} else if (screen == 'report') {
				var newScreen = 'overview';
				if (farmMode == -1) {
					newScreen = lib.hasPA ? 'place' : 'overview_villages';
				} else if (farmMode == 1) {
					newScreen = 'overview_villages';
				} else if (farmMode == 2) {
					newScreen = 'overview';
				} else if (farmMode == 3) {
					if (lib.hasPA) {
						newScreen = 'place';
					} else {
						changeFarmMode(2);
					}
				}
				setLocation(game_data.link_base_pure.replace('screen=', 'screen='+ newScreen), 300, 600);
				return;
			}

			if (farmMode == -1) {
				if (lib.hasPA && screen == 'place') {
					changeFarmMode(3);
				} else {
					changeFarmMode(1);
				}
				if (game_data.village.id == Data.bot.get('startVillageID')) {
					waitBeforeRestart();
					return;
				}
			}

			if (farmMode == 1 && screen == 'overview_villages') {
				var switchTab = false;
				if (lib.hasPA) {
					// check if the right tab is activated...
					var overview_menu = __('overview_menu');
					var selectedCell = _C(overview_menu, 'selected', 0);
					if (selectedCell) {
						var link = _T(selectedCell, 'a', 0);
						var match = link.href.match(/&mode=([^&#]+)/);
						var mode = match && match[1];
						if (mode != 'combined' && mode != 'prod') {
							var firstLink = _T(overview_menu, 'a', 0);
							firstLink.click();
							switchTab = true;
						}
					}
				}
				if (!switchTab) {
					var table = __('combined_table') || __('production_table');
					var rows = table.rows;
					var villIdx = 0;
					var headerCells = _T(table, 'th', 0).parentNode.cells;
					for (var i = 0, max = headerCells.length; i < max && !villIdx; i++) {
						if (texts.regex.village.test(headerCells[i].innerHTML))
							villIdx = i;
					}
					var ownVills = [];
					for (var i = 1, max = rows.length; i < max; i++) {
						var match = rows[i].cells[villIdx].innerHTML.match(texts.regex.ownVill);
						if (match) {
							ownVills.push(parseInt(match[1], 10));
						}
					}
					if (!ownVills.length) {
						lib.error(texts.gui.noVillages);
						deactivateFarmbot();
					} else {
						Data.bot.set('ownVillages', ownVills);
						if (lib.hasPA) {
							changeFarmMode(3);
							setLocation(game_data.link_base_pure.replace('screen=', 'screen=place'), 400, 1000);
						} else {
							changeFarmMode(2);
							setLocation(game_data.link_base_pure.replace('screen=', 'screen=overview'), 500, 900);
						}
					}
				}
			} else if (farmMode == 2 && screen == 'place') {
				nextVillage();
			} else if (farmMode == 2 && (screen == 'overview' || screen == 'overview_villages')) {
				if (lib.hasPA || screen == 'overview') {
					changeFarmMode(3);
					setLocation(game_data.link_base_pure.replace('screen=', 'screen=place'), 400, 1000);
				} else {
					nextVillage();
				}
			} else if (farmMode == 3 && screen == 'place') {
				// check if enough units are there...
				if (__('units_form')) {
					var usableUnits = getUsableUnits();
					var spysAvailable = usableUnits.spy > 0;
					Data.bot.set('spysAvailable', spysAvailable);
					if (usableUnits.normalCount == 0) {
						nextVillage();
					} else {
						if (farmOver == 0) {
							changeFarmMode(4);
							setLocation(game_data.link_base_pure.replace('screen=', 'screen=map'), 400, 800);
						} else if (farmOver == 1) {
							// delay this part to get access to Farmlist:
							setTimeout(function() {
								Farmlist.createLists(true);
								Farmlist.normalList.sort(function(a,b) {
									return a[0] - b[0];
								});
								// get the first element from this list:
								var firstEl = Farmlist.normalList[0];
								if (firstEl) {
									var ID = firstEl[2];
									var coords = Data.villages.getCoords(ID);
									Farmlist.clickCoords(coords);
									setRandom(function() {
										changeFarmMode(7);
										__('target_attack').click();
									}, 1000, 1600);
								} else {
									nextVillage();
								}
							});
						}
					}
				}
			} else if (farmMode == 4 && screen == 'map') {
				setRandom(function() {
					var maxDistance = Data.settings.get('farming_maxDistance');
					var villages = win.TWMap.villages;
					var babas = [];
					var ignore = lib.storage.getValue('autoFarm-ignore', '');
					var spysAvailable = Data.bot.get('spysAvailable');
					var minRes = Data.settings.get('farming_minRes', 0) ? Data.settings.get('farming_minRes', 1) : 0;
					for (var key in villages) {
						if (villages[key].owner == '0') {
							var ID = villages[key].id;
							var cX = Math.floor(villages[key].xy / 1000);
							var cY = villages[key].xy - cX * 1000;

							if (Data.villages.isLocked(ID) ||
								maxDistance[0] && lib.getCoordsDiff(0, [cX, cY], 0) > maxDistance[1] ||
								Data.commands.getTo(ID).length ||
								!Data.villages.existSpy(ID) && !spysAvailable ||	// if there are no more spys in this village
								ignore.indexOf('|'+ ID +'|') > -1 ||				// ignore village with too long runtimes
								Data.villages.exist(ID) && minRes && minRes > Data.villages.getRes(ID).sum)	// too few resources
								continue;

							var dist = lib.getCoordsDiff(0, [cX, cY], 0);
							villages[key].ranking = dist;
							babas.push(villages[key]);
						}
					}
					// deactivate autofarming, load more map data or switch to the next village (used at the moment)
					if (!babas.length) {
						// there is no implementation for loading more map data currently
						nextVillage();
					} else {
						babas.sort(function(a, b) {
							return (a.ranking - b.ranking);
						});
						changeFarmMode(5);

						var targetBB = babas[0];
						var ID = targetBB.id;
						var cX = Math.floor(targetBB.xy / 1000);
						var cY = targetBB.xy - cX * 1000;

						// save everything?!
						Data.villages.setCoords(ID, cX +'|'+ cY);
						Data.villages.setName(ID, targetBB.name);
						Data.villages.setOwnerID(ID, parseInt(targetBB.owner, 10));
						var bonus = targetBB.bonus;
						if (bonus && bonus.length == 2) {
							var match = bonus[1].match(/bonus\/(.+)\.png/);
							if (match) {
								var bon = match[1];
								Data.villages.setBonus(ID, bon);
							}
						}
						Data.villages.save();

						// attack that village!
						setLocation(game_data.link_base_pure.replace('screen=', 'screen=info_village&id=' + ID), 100, 200);
					}
				}, 2000, 8000);
			} else if (farmMode == 5 && screen == 'info_village') {
				setLocation(location.href.replace('info_village&id=', 'place&target='), 600, 1200);
				changeFarmMode(6);
			} else if (farmMode == 6 && screen == 'place') {
				var usableUnits = getUsableUnits();
				if (usableUnits.normalCount == 0) {
					nextVillage();
				} else {
					var match = location.href.match(regex.targetId);
					if (match) {
						win.selectAllUnits(false);
						var ID = match[1];
						var sendUnits = true;
						var sendOnlySpyAttack = true;
						var maxAgeSettings = Data.settings.get('farming_maxAge');
						if (Data.villages.existSpy(ID)) {
							var reportAge = Math.floor(Data.villages.getLastTimeDiff(ID) / 3600);
							if (maxAgeSettings[0] || maxAgeSettings[1] >= reportAge) {
								var sendResTroops = insertFarmTroops(ID, usableUnits);
								// if there are no units ready to send which can carry resources
								if (!sendResTroops) {
									addToIgnoreList(ID);
									sendUnits = false;
								}
								sendOnlySpyAttack = false;
							}
						}

						if (sendOnlySpyAttack) {
							var isBarbarian = Data.villages.isBarbarian(ID);
							var farmSpys = getFarmSpyCount(false, usableUnits.spy, isBarbarian);
							if (farmSpys > 0) {
								__('unit_input_spy').value = farmSpys;
							} else {
								Data.bot.set('spysAvailable', false);
								sendUnits = false;
							}
						}

						setRandom(function() {
							if (sendUnits) {
								changeFarmMode(7);
								__('target_attack').click();
							} else {
								changeFarmMode(4);
								setLocation(game_data.link_base_pure.replace('screen=', 'screen=map'));
							}
						}, 1000, 1600);
					} else {
						setLocation(game_data.link_base_pure.replace('screen=', 'screen=map'), 600, 1200);
					}
				}
			} else if (farmMode == 7 && screen == 'place') {
				if (__('troop_confirm_go')) {
					changeFarmMode(3);
					setRandom(function() {
						__('troop_confirm_go').click();
					}, 300, 700);
				} else {
					// something went wrong...
					addToIgnoreList(ID);
					if (__('error')) {
						// an error? hmm...
						// maybe i have to do something...
					}
					setRandom(function() {
						changeFarmMode(4);
						setLocation(game_data.link_base_pure.replace('screen=', 'screen=map'));
					}, 1000, 1600);
				}
			}
		}
		function addToIgnoreList(ID) {
			var ignore = lib.storage.getValue('autoFarm-ignore', '');
			if (ignore.indexOf('|' + ID + '|') == -1) {
				ignore += '|' + ID + '|';
				lib.storage.setValue('autoFarm-ignore', ignore);
			}
		}


		/*
		 * unknown reports are reports with a
		 * higher reportID than the referenceID
		 */
		function findUnknownReports() {
			getPagelistCell();
			var rows = __('report_list').rows;
			var classCounts = {
				known: 0,
				unknown: 0
			};
			for (var i = 1, max = rows.length-1; i < max; i++) {
				var link = _T(rows[i], 'a', 0);
				if (!link)
					continue;
				var match = link.href.match(regex.viewId);
				if (!match)
					continue;

				var reportID = parseInt(match[1], 10);
				if (reportID <= referenceID) {
					classCounts.known++;
				} else {
					classCounts.unknown++;
				}
			}

			if (!classCounts.known && classCounts.unknown > 0) {
				// all unknown -> forward
				if (limits[1] - limits[0] <= 1 || limits[1] == currentPage) {
					/*
					 * the reports on this page are all unknown
					 * the reports on the next page are all known or this is the last page
					 *	=> change to "findUnreadReports"
					 */
					changeReportMode(2);
					findUnreadReports();
				} else {
					limits[0] = currentPage;
					saveLimits();
					// jump...
					changePage();
				}
			} else if (classCounts.known > 0 && !classCounts.unknown) {
				// all known -> backward
				if (limits[1] - limits[0] <= 1) {
					/*
					 * the reports on this page are all known
					 * the reports on the previous page are all unknown
					 *	=> change to "findUnreadReports"
					 */
					changeReportMode(2);
					findUnreadReports();
				} else {
					if (!start || currentPage > 1 || classCounts.known) {
						limits[1] = currentPage-1;
						saveLimits();
					}
					// jump...
					changePage();
				}
			} else {
				/*
				 * some reports on this page are known, some are unknown
				 *	=> change ot "findUnreadReports"
				 */
				changeReportMode(2);
				findUnreadReports();
			}
		}
		function findUnreadReports() {
			getPagelistCell();
			var rows = __('report_list').rows;
			var lastUnreadLink;
			var newReferenceID = 0;
			for (var i = 1, max = rows.length-1; i < max; i++) {
				var link = _T(rows[i], 'a', 0);
				if (!link)
					continue;
				var match = link.href.match(regex.viewId);
				var reportID = match && parseInt(match[1], 10);
				if (!newReferenceID)
					newReferenceID = reportID;
				if (Data.reports.isNew(reportID)) {
					lastUnreadLink = link;
					newReferenceID = 0;
				}
			}
			if (newReferenceID) {
				referenceID = newReferenceID;
				Data.bot.set('referenceID', referenceID);
				Data.bot.save();
			}

			if (lastUnreadLink) {
				// clickIt!
				changeReportMode(3);
				Data.bot.set('continuesReading', 1);
				Data.bot.save();
				clickReportLink(lastUnreadLink);
			} else {
				// there are only read reports
				limits[0] = limits[1] = currentPage-1;
				saveLimits();
				changePage();
			}
		}
		function clickNextReport(now) {
			if (continuesReading) {
				var match = location.href.match(regex.viewId);
				var reportID = match && parseInt(match[1], 10);
				if (reportID) {
					Data.bot.set('referenceID', reportID);
					Data.bot.save();
				}
			}
			var links = _T(__('content_value'), 'a');
			var nextReportLink;
			// search the link containing "<<"
			for (var i = 0, max = links.length; i < max && !nextReportLink; i++) {
				var link = links[i];
				if (link.text.indexOf(texts.gui.reportNext) > -1) {
					nextReportLink = link;
				}
			}
			if (!nextReportLink) {
				// no more reports to read...
				deactivateReadMode(true);
			} else {
				if (now) {
					nextReportLink.click();
				} else {
					setRandom(function() {
						nextReportLink.click();
					}, 400, 1300);
				}
			}
		}

		function deleteOldReports() {
			getPagelistCell();
			if (currentPage != limits[0]) {
				if (limits[0] > maxPageNum) {
					limits[0] = limits[1] = maxPageNum;
					saveLimits();
				}
				changePage();
				return;
			}
			var rows = __('report_list').rows;
			var count = 0;
			for (var i = 1, max = rows.length-1; i < max; i++) {
				var row = rows[i];
				var link = _T(row, 'a', 0);
				if (!link)
					continue;
				var match = link.href.match(regex.viewId);
				var reportID = match && parseInt(match[1], 10);
				var checkbox = _T(row, 'input', 0);
				if (checkbox && checkbox.type == 'checkbox') {
					var isOld = Data.reports.isOld(reportID);
					checkbox.checked = isOld;
					if (isOld) {
						count++;
					}
				}
			}

			if (count) {
				setRandom(function() {
					var inputs = _T(_next(__('report_list')), 'input');
					var delInput;
					for (var i = 0, max = inputs.length; i < max && !delInput; i++) {
						var input = inputs[i];
						if (input.name == 'del') {
							delInput = input;
						}
					}
					limits[0] = limits[1] = currentPage-1;
					saveLimits();
					delInput.click();
				}, 400, 1000);
			} else {
				// previous page
				limits[0] = limits[1] = currentPage-1;
				saveLimits();
				if (limits[0]) {
					changePage();
				} else {
					// no more pages...
					deactivateReadMode(true);
				}
			}
		}


		// public methods
		function setRandom(func, min, max) {
			var randomTimeout = Math.floor(min + Math.random() * (max - min));
			window.setTimeout(function() {
				if (!reportMode && !farmMode)
					return;

				var pauseOverlay = __('dsplus_pauseOverlay');
				var fInt = null;
				var fn = function() {
					if (!paused || !reportMode && !farmMode) {
						if (fInt) {
							window.clearInterval(fInt);
						}
						if (reportMode || farmMode) {
							func.apply(Bot, arguments);
						}
						_hide(pauseOverlay);
					}
				};

				if (paused) {
					fInt = window.setInterval(fn, 100);

					if (pauseOverlay) {
						_show(pauseOverlay);
					} else {
						pauseOverlay = _ce('div', '', {
							'style': 'width: '+ window.innerWidth +'px; height: '+ window.innerHeight +'px; position: fixed; top: 0px; left: 0px; z-index: -1; background-color: #002; opacity: 0.5;'
						});
						var firstNode = doc.body.firstChild;
						_before(pauseOverlay, firstNode);
					}
				} else {
					fn();
				}
			}, randomTimeout);
			return randomTimeout;
		}
		function setLocation(url, min, max) {
			// to simulate human manner
			if (typeof(min) != 'undefined' && typeof(max) != 'undefined') {
				setRandom(function() {
					location.href = url;
				}, min, max);
			} else {
				// to make sure location.href is available:
				var script = document.body.appendChild(document.createElement('script'));
				script.type = 'text/javascript';
				script.innerHTML = '$(document).ready(function(){ window.location.href="' + url + '"; });';
			}
		}
		function deactivateReadMode(botTriggered) {
			if (start && botTriggered) {
				if (reportMode == 4)
					lib.alert(texts.gui.nothingToDelete);
				else
					lib.alert(texts.gui.nothingToRead);
			}
			changeReportMode(0);
			Data.bot.set('continuesReading', 0);
			Data.bot.save();
			if (farmMode) {
				// do stuff here...
				farmbot();
			}
		}
		function deactivateFarmbot() {
			changeFarmMode(0);
			if (autoFarm_button)
				autoFarm_button.classList.remove('pause');
		}

		function insertReportButtons() {
			var report_list = __('report_list');
			var firstRowCells = report_list.rows[0].cells;
			var countedColSpan = 1;		// plus the "DS+"-Cell
			for (var i = 0; i < firstRowCells.length; i++) {
				countedColSpan += firstRowCells[i].colSpan || 1;
			}

			var row = report_list.insertRow(0);
			var cell = row.insertCell(-1);
			cell.colSpan = countedColSpan;

			var autoCleaning_button = _ce('input', '', {
				'id': 'dsplus_autoCleaning',
				'type': 'button',
				'class': 'btn btn-cancel',
				'value': texts.gui.delOldReports
			});
			autoCleaning_button.addEventListener('click', startReportCleaning, true);
			cell.appendChild(autoCleaning_button);

			var autoReading_button = _ce('input', '', {
				'id': 'dsplus_autoReading',
				'type': 'button',
				'class': 'btn',
				'value': texts.gui.readReports
			});
			autoReading_button.addEventListener('click', startReportReading, true);
			cell.appendChild(autoReading_button);
		}
		function startReportReading() {
			if (reportOverview) {
				limits = [0, 1];
				getPagelistCell();
				if (maxPageNum)
					limits[1] = maxPageNum;

				saveLimits();
				changeReportMode(1);
				start = true;
				findUnknownReports();
			} else {
				var match = location.href.match(regex.viewId);
				var reportID = match && parseInt(match[1], 10);
				if (reportID == referenceID) {
					Data.bot.set('continuesReading', 1);
					Data.bot.save();
				}
				changeReportMode(3);
				clickNextReport(true);
			}
		}
		function startReportCleaning() {
			limits = [1, 1];
			getPagelistCell();
			if (maxPageNum)
				limits[0] = limits[1] = maxPageNum;

			saveLimits();
			changeReportMode(4);
			start = true;
			deleteOldReports();
		}
		function toggleFarmbot(newTab) {
			if (farmMode) {
				deactivateFarmbot();
			} else {
				if (Data.settings.get('bot_farmOver') == 2 && !game_data.player.farm_manager)
					return farm_manager_needed();
				var activate = function() {
					setTimeout(function() {
						var doNotShowAgainCheckbox = _C(__('confirmation-box'), 'doNotShowAgain', 0);
						if (doNotShowAgainCheckbox) {
							var doNotShowAgain = doNotShowAgainCheckbox.checked;
							Data.bot.set('hideWarning', doNotShowAgain);
						}

						Data.bot.set('startVillageID', game_data.village.id);
						changeFarmMode(1);
						if (!newTab) {
							overviewLink.click();
						}
					});
				};
				if (newTab || Data.bot.get('hideWarning')) {
					activate();
				} else {
					var buttons = [{
						text: texts.gui.OK,
						callback: activate,
						confirm: true
					}];
					var dontShowAgain = '<br><span style="font-size: small;"><input type="checkbox" class="doNotShowAgain">'+ texts.gui.doNotShowAgain +'</span>';
					UI.ConfirmationBox(texts.gui.autoFarm.warning + dontShowAgain, buttons);
				}
			}
		}
		function pause() {
			paused = true;
		}
		function resume() {
			if (__('dsplus_settings')) {
				window.setTimeout(function() {
					if (!__('dsplus_settings')) paused = false;
				}, 500);
			} else {
				paused = false;
			}
		}

		// Bot
		return {
			active_protection: active_protection,
			activeFarmBot: !!farmMode,
			pause: pause,
			resume: resume,
			dynamicLoad: dynamicLoad,
			setRandom: setRandom,
			setLocation: setLocation,
			deactivateReadMode: deactivateReadMode,
			deactivateFarmbot: deactivateFarmbot,
			insertReportButtons: insertReportButtons,
			startReportReading: startReportReading,
			startReportCleaning: startReportCleaning,
			toggleFarmbot: toggleFarmbot
		};
	})();


	var Template = {
		// columns for the unit templates
		unitCols: {},
		// columns for the building templates
		buildCols: {},
		unitNum: Data.templates.getTemplateCount('unit'),
		buildNum: Data.templates.getTemplateCount('build'),
		initUnitCols: function() {
			Template.unitCols.order = [];

			Template.unitCols.order.push('name');
			Template.unitCols.name = {
				cellAttr: 'class="nowrap" style="text-align: left;"',
				head: texts.gui.name,
				cellShow: '%val% <a class="edit-icon" href="javascript:;"><img alt="#" src="graphic/rename.png"></a>',
				cellEdit: '<input type="text" class="dsplus_name" size="4" value="%val%"><input type="button" value="'+ texts.gui.OK +'">',
				'default': '',
				grey: false,
				factorIntoPop: false
			};
			var width = {
				spy: ' width: 20px;',
				ram: ' width: 20px;',
				catapult: ' width: 20px;',
				knight: ' ;',
				snob: ' ;',
				other: ' width: 25px;'
			};
			for (var key in lib.unitInfo) {
				if (!lib.unitInfo[key].pop)
					continue;
				Template.unitCols.order.push(key);
				Template.unitCols[key] = {
					cellAttr: 'class="nowrap" style="text-align: center;"',
					head: '<img class="tooltip" src="'+ lib.unitImgPath.replace('%unit%', key) +'" alt="'+ texts.unit[key] +'" title="'+ texts.unit[key] +'">',
					cellShow: '%val%',
					cellEdit: '<input type="text" class="dsplus_'+ key +'" size="1" maxlength="5" value="%val%" style="text-align: center;'+ (width[key] || width.other) +'">',
					'default': 0,
					grey: true,
					factorIntoPop: true
				};
			}
			Template.unitCols.order.push('free_pop');
			Template.unitCols.free_pop = {
				cellAttr: 'class="nowrap" style="text-align: left;"',
				head: texts.gui.freePopuplation,
				cellShow: '%val%',
				cellEdit: '<input type="text" class="dsplus_free_pop" size="3" maxlength="6" value="%val%">',
				'default': 0,
				grey: true,
				factorIntoPop: true
			};
			Template.unitCols.order.push('pop');
			Template.unitCols.pop = {
				cellAttr: 'class="nowrap" style="text-align: left;"',
				head: '<span class="icon header population tooltip" title="'+ texts.gui.population +'"> </span>',
				cellShow: '%val%',
				cellEdit: '<span class="dsplus_pop">%val%</span>',
				'default': 0,
				grey: false,
				factorIntoPop: false
			};
			Template.unitCols.order.push('cancel');
			Template.unitCols.cancel = {
				cellAttr: 'class="nowrap" style="text-align: center;"',
				head: '&nbsp;',
				cellShow: '<a class="cancel-icon solo tooltip" href="javascript:;" title="'+ texts.gui['delete'] +'"></a>',
				cellEdit: '<a class="cancel-icon solo tooltip" href="javascript:;" title="'+ texts.gui['delete'] +'"></a>',
				'default': '',
				grey: false,
				factorIntoPop: false
			};

			Template.unit = Template.unitCols;
		},
		initBuildCols: function() {
			Template.buildCols.order = [];

			Template.buildCols.order.push('name');
			Template.buildCols.name = {
				cellAttr: 'class="nowrap" style="text-align: left;"',
				head: texts.gui.name,
				cellShow: '%val% <a class="edit-icon" href="javascript:;"><img alt="#" src="graphic/rename.png"></a>',
				cellEdit: '<input type="text" class="dsplus_name" size="4" value="%val%"><input type="button" value="'+ texts.gui.OK +'">',
				'default': '',
				grey: false,
				factorIntoPop: false
			};
			for (var key in lib.buildingInfo) {
				if (!lib.buildingInfo.hasOwnProperty(key))
					continue;
				Template.buildCols.order.push(key);
				Template.buildCols[key] = {
					cellAttr: 'class="nowrap" style="text-align: center;"',
					head: '<img class="tooltip" src="'+ lib.buildingImgPath.replace('%building%', key) +'" alt="'+ texts.building[key] +'" title="'+ texts.building[key] +'">',
					cellShow: '%val%',
					cellEdit: '<input type="text" class="dsplus_'+ key +'" size="1" maxlength="5" value="%val%" style="text-align: center;">',
					'default': lib.buildingInfo[key].min_level,
					grey: true,
					factorIntoPop: true
				};
			}
			Template.buildCols.order.push('points');
			Template.buildCols.points = {
				cellAttr: 'class="nowrap" style="text-align: left;"',
				head: texts.gui.points,
				cellShow: '%val%',
				cellEdit: '<span class="dsplus_points">%val%</span>',
				'default': 0,
				grey: false,
				factorIntoPop: false
			};
			Template.buildCols.order.push('pop');
			Template.buildCols.pop = {
				cellAttr: 'class="nowrap" style="text-align: left;"',
				head: '<span class="icon header population tooltip" title="'+ texts.gui.population +'"> </span>',
				cellShow: '%val%',
				cellEdit: '<span class="dsplus_pop">%val%</span>',
				'default': 0,
				grey: false,
				factorIntoPop: false
			};
			Template.buildCols.order.push('cancel');
			Template.buildCols.cancel = {
				cellAttr: 'class="nowrap" style="text-align: center;"',
				head: '&nbsp;',
				cellShow: '<a class="cancel-icon solo tooltip" href="javascript:;" title="'+ texts.gui['delete'] +'"></a>',
				cellEdit: '<a class="cancel-icon solo tooltip" href="javascript:;" title="'+ texts.gui['delete'] +'"></a>',
				'default': '',
				grey: false,
				factorIntoPop: false
			};

			Template.build = Template.buildCols;
		},
		// ab hier neu...
		init: function() {
			var allowedRecruitScreens = {
				barracks: true,
				stable: true,
				garage: true,
				train: true
			};
			if (Data.settings.get('template_buildings_enabled') &&
				screen == 'main' ||
				(screen == 'overview_villages' && __('buildings_table'))
			) {
				Template.initBuildCols();

				var mass = (screen == 'overview_villages');
				if (mass) {
					if (win.BuildingOverview) {
						win.BuildingOverview.generate_buildings_for_village = Template.generate_buildings_for_village;
						win.BuildingOverview.upgrade_building = Template.upgrade_building;
					}

					Template.doBuildMassTemplate();

					Filter.observerList.push(function(initialize) {
						if (!initialize)
							Template.doBuildMassTemplate();
					});
				} else if (__('buildings')) {
					Template.doBuildTemplate();
				}
			} else if (Data.settings.get('template_units_enabled') && allowedRecruitScreens[screen]) {
				Template.initUnitCols();

				var mode = game_data.mode;
				var mass = (mode == 'mass' || mode == 'mass_decommission');

				if (mass) {
					Template.doUnitMassTemplate();
				} else if (__('train_form')) {
					Template.doUnitTemplate();
				}
			}
		},


		/*
		 * building template section
		 */
		doBuildTemplate: function() {
			var selectCell = __('buildings').rows[0].cells[0];
			var initialize = Template.insertTemplateSelect('build', selectCell);

			var train = game_data.mode != 'decommission';
			Template.updateBuildLinks();

			if (initialize) {
				win.BuildingMain.update_all = Template.update_all;
			}
		},
		doBuildMassTemplate: function() {
			if (__('buildings_table')) {
				win.BuildingOverview.show_all_upgrade_buildings = function() {};

				var init = !__('dsplus_template_header');
				if (init) {
					var header = __('buildings_table').rows[0].cells;
					_remove(_slice(header, 0, 2));

					var newHeader = _ce('th',
						texts.gui.templates +'&nbsp;<a href="javascript:;" class="edit-icon"><img src="graphic/rename.png" alt="#"></a>',
						{
							id: 'dsplus_template_header',
							'class': 'nowrap'
						}
					);
					_before(newHeader, header[header.length-1]);

					var editLink = newHeader.firstElementChild;
					editLink.addEventListener('click', function() {
						var HTML = '<h5 style="text-align: center;">'+ texts.gui.buildTemplateHeader +'</h5>' +
						'<table class="vis" style="width: 100%;"><tbody id="dsplus_build_templates"><tr>';
						for (var i = 0, max = Template.buildCols.order.length; i < max; i++) {
							var key = Template.buildCols.order[i];
							HTML += '<th ' + Template.buildCols[key].cellAttr + '>' + Template.buildCols[key].head + '</th>';
						}
						HTML += '</tr>';

						for (var i = 0, max = Data.templates.get('build_templates').length; i < max; i++) {
							HTML += '<tr id="build_'+ i +'">' + Template.createTemplateRow('build', i) + '</tr>';
						}
						HTML += '</tbody></table>' +
						'<div style="text-align: center;"><a href="javascript:;">'+ texts.gui.addBuildTemplate +'</a></div>';
						var pos, width;
						var attachEvents = !__('build_templates');

						var firstRow = __('buildings_table').rows[0];
						pos = jQuery(firstRow).offset();
						pos.top += jQuery(firstRow).outerHeight() + 2;
						width = jQuery(firstRow).outerWidth() - 4;

						lib.inlinePopup('build_templates', pos.left, pos.top, HTML, true, width, 'auto');
						recreateToolTip();

						if (attachEvents) {
							var dsplus_build_templates = __('dsplus_build_templates');
							Template.attachRowEvents('build', _slice(dsplus_build_templates.rows, 1));

							// add template
							var addLink = _T(__('build_templates'), 'a', 'last');
							addLink.addEventListener('click', function() {
								var newRow = _ce('tr', Template.createTemplateRow('build', null, true));
								dsplus_build_templates.appendChild(newRow);

								var appendedRow = dsplus_build_templates.rows[dsplus_build_templates.rows.length-1];
								Template.calcPop('build', appendedRow);
								Template.calcPoints(appendedRow);
								Template.attachRowEvents('build', appendedRow);
							}, false);
						}
					}, true);
				}

				var rows = __('villages').rows;
				for (var i = 0, row; row = rows[i]; i++) {
					if (init) {
						_remove(_C(row, 'build_icon', 0));
						_remove(_C(row, 'destroy_icon', 0));
					}

					if (row.style.display == 'none' || _TC(row, 'select', 'build_templateselect', 0))
						continue;

					var villageID = row.id.match(/v_(\d+)/)[1];
					var newCell = _ce('td', Template.createTemplateSelect('build', null, false, null, true, villageID));
					var templateSelect = _T(newCell, 'select', 0);
					_before(newCell, row.cells[row.cells.length-1]);
					templateSelect.addEventListener('change', Template.changeTemplate, true);
				}

				Template.updateBuildOverviewLinks();
			}
		},
		update_all: function (data) {
			if (data.reload) {
				document.location.reload();
				return;
			}
			var $queue_wrapper = jQuery('#buildqueue_wrap');
			var $queueEl;
			if (data.building_orders) {
				$queueEl = jQuery(data.building_orders);
				var queueEl = $queueEl.get(0);
				var queueTBody = _T(queueEl, 'tbody', 0);
				var queueInfo;
				if (queueTBody && !_C(queueTBody, 'dsplus_queue_hide').length) {
					queueInfo = Queues.analyse(queueEl);
					if (Data.settings.get('queue_option'))
						Queues.reorderQueue(queueEl, queueTBody, queueInfo);
				}
			}
			if ($queue_wrapper.length == 1) {
				if (data.building_orders) {
					$queue_wrapper.replaceWith($queueEl);
				} else {
					$queue_wrapper.remove();
				}
			} else {
				jQuery('#building_wrapper').before($queueEl);
			}
			if (data.next_buildings) {
				jQuery('#building_wrapper').replaceWith(data.next_buildings);
				jQuery('.inactive img').fadeTo(0, .5);
			}
			if (typeof(data.confirm_queue) != 'undefined') {
				win.BuildingMain.confirm_queue = data.confirm_queue;
			}
			if (typeof(data.population) != 'undefined') {
				__('pop_current_label').innerHTML = data.population;
			}
			Template.doBuildTemplate();
			win.startTimer();
			if (typeof(win.QuestArrows) != 'undefined') {
				win.QuestArrows.init();
			}
			if (win.hasOwnProperty('OrderProgress'))
				win.OrderProgress.initProgress();
			Storage.updateInfoRes();
			recreateToolTip();
		},
		buildInputKeyUp: function(e) {
			// tab
			if (e.keyCode == 9)
				return;

			if (regex.NaN.test(this.value)) {
				var pos = getCursorPosition(this);
				var count = this.value.substr(0, pos).match(/[^\d]/g).length;
				this.value = this.value.replace(/[^\d]/g, '');
				setCursorPosition(this, pos-count);
			}

			var build = this.className.match(/dsplus_(.+)/)[1];
			var val = this.value || 0;
			if (val !== 0) {
				val = parseInt(val, 10);
				if (val < lib.buildingInfo[build].min_level)
					this.value = lib.buildingInfo[build].min_level;
				if (val > lib.buildingInfo[build].max_level)
					this.value = lib.buildingInfo[build].max_level;
			}

			if (build == 'church' || build == 'church_f') {
				var otherChurch = {
					'church': 'church_f',
					'church_f': 'church'
				};
				var row = this.parentNode.parentNode;
				var otherChurchInput = _C(row, 'dsplus_'+ otherChurch[build], 0);
				otherChurchInput.disabled = false;
				otherChurchInput.style.color = '';
				if (val && otherChurchInput.value && otherChurchInput.value != '0') {
					this.style.color = 'red';
					otherChurchInput.style.color = 'red';
				} else if (val)
					otherChurchInput.disabled = true;
			}

			Template.calcPop('build', this.parentNode.parentNode);
			Template.calcPoints(this.parentNode.parentNode);
		},
		buildInputChange: function() {
			if (!this.value)
				this.value = 0;
		},
		buildInputClick: function() {
			if (!this.value)
				this.value = 0;
			if (this.value == '0') {
				this.focus();
				this.select();
			}
		},
		buildInputBlur: function() {
			if (!this.value)
				this.value = 0;
			// the empty object is for the expected parameter e
			Template.buildInputKeyUp.call(this, {});
		},
		updateBuildLinks: function() {
			var show_all_buildings = __('buildings_unmet');
		//	var hide_completed = __('dsplus_hide_completed').checked;
			var hide_completed = false;
			var noTemplateSelected = Template.getSelectedIndex('build') < 0;
			var resNow = {
				wood: parseInt(__('wood').innerHTML, 10),
				stone: parseInt(__('stone').innerHTML, 10),
				iron: parseInt(__('iron').innerHTML, 10)
			};
			var matches = {
				wood: __('wood').title.match(/\d+/),
				stone: __('stone').title.match(/\d+/),
				iron: __('iron').title.match(/\d+/)
			};
			var resProd = {
				wood: matches.wood && parseInt(matches.wood[0], 10) || 5,
				stone: matches.stone && parseInt(matches.stone[0], 10) || 5,
				iron: matches.iron && parseInt(matches.iron[0], 10) || 5
			};


			var buildingRows = __('buildings').rows;
			for (var i = 1, row; row = buildingRows[i]; i++) {
				// make everything visible in this row
				_show(row);
				var cells = row.cells;
				if (cells.length < 3) {
					var isToggleRow = row.classList.contains('togglerow');
					if (isToggleRow && !show_all_buildings || !isToggleRow && hide_completed && noTemplateSelected)
						_hide(row);
				} else {
					for (var j = 1, cell; cell = cells[j]; j++) {
						if (cell.classList.contains('buildReady') || cell.classList.contains('buildOver')) {
							_hide(cell);
						} else {
							_show(cell);
						}
					}
				}

				if (cells.length < 2)
					continue;

				var link = _T(row, 'a', 0);
				var build = link.href.match(/screen=([^&=]+)/)[1];
				var level = row.cells[0].lastElementChild.textContent.replace(/[^\d]/g, '') || 0;
				level = parseInt(level, 10);
				Village.buildings[build] = level;
				if (Queues.buildings[build]) {
					level += Queues.buildings[build].levelUp;
					level -= Queues.buildings[build].levelDown;
				}
				var max_level = noTemplateSelected ? lib.buildingInfo[build].max_level : Template.getSelectedTemplate('build', null, build);

				if (level == max_level && hide_completed) {
					_hide(row);
				} else if (level >= max_level) {
					var insertIt = true;
					var searchFor = level > max_level ? 'buildOver' : 'buildReady';

					for (var j = 1, cell; cell = cells[j]; j++) {
						if (cell.classList.contains(searchFor)) {
							_show(cell);
							insertIt = false;
						} else
							_hide(cell);
					}
					if (insertIt) {
						if (level == max_level) {
							var cell = _ce('td', texts.gui.buildReady, {
								align: 'center',
								'class': 'inactive buildReady',
								colspan: '6'
							});
							row.appendChild(cell);
						} else {
							var cell = _ce('td', texts.gui.buildOver, {
								align: 'center',
								'class': 'warn buildOver',
								colspan: '5',
								style: 'font-weight: bold;'
							});
							row.appendChild(cell);

							var tearDownLink = _ce('a', texts.gui.tearDownLevel, {
								'class': 'btn '+ (Queues.tearDownCount < 5 ? '' : 'btn-disabled'),
								'href': 'javascript:;'
							});
							tearDownLink.addEventListener('click', Template.destroy, true);

							var cell = _ce('td', '', { 'class': 'buildOver' });
							cell.appendChild(tearDownLink);
							row.appendChild(cell);
						}
					}
				} else {
					var resNext = Template.calcRes(build, level);

					for (var res in resNow) {
						var diff = Math.max(resNext[res] - resNow[res], 0);
						var span = _C(row, res, 0);
						var spanParent = span && span.parentNode;
						if (!spanParent)
							continue;
						var spans = _T(spanParent, 'span');
						var neededResSpan = spans[1];
						if (!neededResSpan) {
							var space = _text(' ');
							spanParent.appendChild(space);

							neededResSpan = _ce('span', '', {
								'class': 'neededResources'
							});
							spanParent.appendChild(neededResSpan);
						}

						if (diff > 0) {
							var time = diff / resProd[res];
							var hours = Math.floor(time);
							var minutes = Math.floor((time - hours) * 60);
							var seconds = Math.floor(((time - hours) * 60 - minutes) * 60);

							neededResSpan.title = hours +':'+ lib.padLeft(minutes, '0', 2) +':'+ lib.padLeft(seconds, '0', 2);
							neededResSpan.classList.remove('inactive');
						} else {
							neededResSpan.title = '';
							neededResSpan.classList.add('inactive');
						}
						neededResSpan.textContent = '('+ diff +')';
					}
				}
			}
		},
		generate_buildings_for_village_init: false,
		updateBuildOverviewLinks: function() {
			var interval = window.setInterval(function() {
				if (typeof(win.BuildingOverview.page_size) == 'undefined')
					return;
				Template.generate_buildings_for_village_init = true;

				win.BuildingOverview._display_all = true;
				win.BuildingOverview._display_type = 0;
				var get_all_possible_building_upgrades_link = __('get_all_possible_building_upgrades_link').value;
				var build_villages, destroy_villages;
				var generate_buildings = function() {
					for (var id in build_villages) {
						if (!build_villages[id].managed_by_am) {
							win.BuildingOverview._display_type = 0;

							Template.generate_all_buildings_for_village(build_villages[id], destroy_villages[id], id);
						}
					}
					Template.generate_buildings_for_village_init = false;
				};
				jQuery.getJSON(
					get_all_possible_building_upgrades_link,
					{
						destroy: 0,
						page_start: win.BuildingOverview.page_start,
						page_size: win.BuildingOverview.page_size,
						order: win.BuildingOverview.order,
						dir: win.BuildingOverview.dir
					},
					function(villages) {
						build_villages = villages;

						if (destroy_villages)
							generate_buildings();
					}
				);
				jQuery.getJSON(
					get_all_possible_building_upgrades_link,
					{
						destroy: 1,
						page_start: win.BuildingOverview.page_start,
						page_size: win.BuildingOverview.page_size,
						order: win.BuildingOverview.order,
						dir: win.BuildingOverview.dir
					},
					function(villages) {
						destroy_villages = villages;

						if (build_villages)
							generate_buildings();
					}
				);
				win.BuildingOverview.update_paged_nav();

				window.clearInterval(interval);
			}, 50);
		},

		generate_all_buildings_for_village: function(build_village, destroy_village, id) {
			win.BuildingOverview._upgrade_villages[id] = build_village;
			var village_row = __('v_'+ id);
			// iognore all hidden rows
			if (village_row.style.display == 'none')
				return;

			// read the Queue
			var buildChanges = Queues.readBuildingOverviewRow(village_row);

			var cells = village_row.cells;
			var noTemplateSelected = Template.getSelectedIndex('build', id) < 0;
			var template = Template.getSelectedTemplate('build', id);
			for (var i = 2, cell; cell = cells[i]; i++) {
				if (!cell.classList.contains('upgrade_building'))
					continue;

				var build = cell.className.replace(/.*b_([^\s]+).*/, '$1');
				var level = parseInt(cell.children.length ? cell.children[0].innerHTML : cell.innerHTML, 10);
				if (isNaN(level))
					level = 0;
				var actLevel = level + (buildChanges[build] || 0);
				var max_level = parseInt(noTemplateSelected ? lib.buildingInfo[build].max_level : template[build], 10);

				var removeLinks = false;
				var destroy = -1;
				var village;
				cell.classList.remove('hidden');
				if (actLevel > max_level) {
					cell.style.backgroundColor = '#ff7070';
					cell.style.color = 'red';
					cell.style.fontWeight = 'bold';

					destroy = 1;
					village = destroy_village;
				} else if (actLevel < max_level) {
					cell.style.backgroundColor = '#aaffaa';
					cell.style.color = 'green';
					cell.style.fontWeight = 'bold';

					destroy = 0;
					village = build_village;
				} else {
					cell.style.backgroundColor = '';
					cell.style.color = '';
					cell.style.fontWeight = '';

					if (!actLevel) cell.classList.add('hidden');
					removeLinks = true;
				}

				var firstSpan = _T(cell, 'span', 0);
				if (firstSpan) {
					cell.appendChild(firstSpan.firstChild);
					_remove(firstSpan);
				}

				if (destroy != -1) {
					if (typeof(village.buildings[build]) != 'undefined') {
						var building_level = parseInt(cell.innerHTML.replace(/<.*>(\d*?)<\/.*?>$/, '$1'), 10);
						var title = win.UpgradeBuilding.generateResourcesLabel(village.buildings[build], destroy);
						var upgrade_anker = _ce('a', building_level, {
							'href': '#',
							'class': 'building_tooltip d_'+ destroy,
							'tooltip': title
						});
						_remove(cell.firstChild);
						cell.appendChild(upgrade_anker);
					} else
						removeLinks = true;
				}

				if (removeLinks) {
					var firstLink = _T(cell, 'a', 0);
					if (firstLink) {
						cell.appendChild(firstLink.firstChild);
						_remove(firstLink);
					}
				}
			}
		},
		/*
		 * injected functions
		 */
		generate_buildings_for_village: function (village, destroy) {
			win.BuildingOverview._upgrade_villages[village.village_id] = village;
			var village_row = __('v_'+ village.village_id);
			// iognore all hidden rows
			if (village_row.style.display == 'none')
				return;

			setTimeout(function() {
				// read the Queue
				var buildChanges = {};
				var cells = village_row.cells;
				var lastCell = cells[cells.length-1];
				var lis = _T(lastCell, 'li');
				for (var i = 0, li; li = lis[i]; i++) {
					var order_status_light = li.firstElementChild;
					var down = order_status_light.style.backgroundColor == 'red';

					var buildingImg = li.lastElementChild.firstElementChild;
					var match = buildingImg.src.match(/buildings\/(.+)\.png/);
					if (!match)
						return;
					var build = match[1];
					if (!buildChanges[build])
						buildChanges[build] = 0;
					buildChanges[build] += down ? -1 : 1;
				}

				var noTemplateSelected = Template.getSelectedIndex('build', village.village_id) < 0;
				var template = Template.getSelectedTemplate('build', village.village_id);
				for (var i = 2, cell; cell = cells[i]; i++) {
					if (!cell.classList.contains('upgrade_building'))
						continue;

					var build = cell.className.replace(/.*b_([^\s]+).*/, '$1');
					var level = parseInt(cell.textContent, 10);
					if (isNaN(level))
						level = 0;
					var actLevel = level + (buildChanges.hasOwnProperty(build) ? buildChanges[build] : 0);
					var max_level = parseInt(noTemplateSelected ? lib.buildingInfo[build].max_level : template[build], 10);

					var firstSpan = _T(cell, 'span', 0);
					if (firstSpan)
						cell.innerHTML = firstSpan.innerHTML;

					if (actLevel > max_level) {
						cell.style.backgroundColor = '#ff7070';
						cell.style.color = 'red';
						cell.style.fontWeight = 'bold';
					} else if (actLevel < max_level) {
						cell.style.backgroundColor = '#aaffaa';
						cell.style.color = 'green';
						cell.style.fontWeight = 'bold';
					} else {
						cell.style.backgroundColor = '';
						cell.style.color = '';
						cell.style.fontWeight = '';

						var firstLink = _T(cell, 'a', 0);
						if (firstLink)
							cell.innerHTML = firstLink.innerHTML;
					}

					var id = build;
					if (destroy && actLevel > max_level || !destroy && actLevel < max_level) {
						if (typeof(village.buildings[id]) != 'undefined') {
							var building_level = parseInt(cell.innerHTML.replace(/<.*>(\d*?)<\/.*?>$/, '$1'), 10);
							var title = win.UpgradeBuilding.generateResourcesLabel(village.buildings[id], destroy);
							var upgrade_anker = '<a href="#" class="building_tooltip d_'+ destroy +'" tooltip="'+ title +'">'+ building_level +'</a>';
							cell.innerHTML = upgrade_anker;
						} else {
							var firstLink = _T(cell, 'a', 0);
							if (firstLink)
								cell.innerHTML = firstLink.innerHTML;
						}
					}
				}
			});
		},
		upgrade_building: function (village_id, building, destroy) {
			var handleAddToQueue = function() {
				var id = (destroy ? 'down' : 'up') +'grade_building_link';
				var url = __(id).value.replace(/village=([0-9]*)/, 'village='+ village_id);
				jQuery.ajax({
					dataType: 'json',
					type: 'get',
					url: url,
					data: {
						id: building,
						force: 1,
						source: game_data.village.id
					},
					success: function (ret) {
						if (ret.error) {
							UI.ErrorMessage(ret.error, 2000);
						} else if (ret.success) {
							win.BuildingOverview._upgrade_villages[village_id].confirm_queue = ret.confirm_queue;
							win.BuildingOverview.generate_buildings_for_village(ret.next_buildings, destroy);
							if (!__('building_order_'+ village_id)) {
								var list = _ce('ul', '', {
									'class': 'order_queue',
									id: 'building_order_'+ village_id
								});
								win.BuildingOverview.create_sortable(list);
								var cells = __('v_'+ village_id).cells;
								var lastCell = cells[cells.length-1];
								lastCell.appendChild(list);
							}
							__('building_order_'+ village_id).innerHTML = ret.building_orders;
						}
					}
				});
			};
			if (win.BuildingOverview._upgrade_villages[village_id].confirm_queue) {
				var msg = texts.gui.extraCost_msg;
				var buttons = [{
					text: texts.gui.OK,
					callback: handleAddToQueue,
					confirm: true
				}];
				UI.ConfirmationBox(msg, buttons);
			} else
				handleAddToQueue();
		},
		destroy: function() {
			if (this.classList.contains('btn-disabled'))
				return;

			var row = this.parentNode.parentNode;
			var buildLink = _T(row, 'a', 0);
			var building_id = buildLink.href.match(/screen=([^&]+)/)[1];
			var current_request_id = ++win.BuildingMain.request_id;
			var data = {
				id: building_id,
				force: 1,
				destroy: 1,
				source: game_data.village.id
			};
			jQuery.ajax({
				url: win.BuildingMain.downgrade_building_link,
				dataType: 'json',
				data: data,
				success: function (build_ret) {
					if (build_ret.error) {
						UI.ErrorMessage(build_ret.error, 2000)
					} else if (build_ret.success && current_request_id > win.BuildingMain.last_request_id) {
						win.BuildingMain.last_request_id = current_request_id;
						// to prevent updating of the upgrade-links to downgrade-links
						delete build_ret.next_buildings;
						win.BuildingMain.update_all(build_ret);
					}
				}
			});
			return false;
		},



		/*
		 * unit template section
		 */
		doUnitTemplate: function() {
			// __('train_form') is a "form"-element...
			var selectCell = _T(__('train_form'), 'tr', 0).cells[0];
			var initialize = Template.insertTemplateSelect('unit', selectCell);

			var train = game_data.mode != 'decommission';
			if (Template.getSelectedIndex('unit') > -1 && train)
				Template.createFillInButton();

			Template.updateUnitSum();
			if (train)
				Template.updateUnitLinks();

			if (initialize) {
				__('pop_current_label').addEventListener('DOMNodeInserted', function() {
					Template.doUnitTemplate();
					Storage.updateInfoRes();
				}, false);
			}
		},
		doUnitMassTemplate: function() {
			// Mass recruitment...
		},
		createFillInButton: function() {
			if (Template.getSelectedIndex('unit') < 0) {
				_remove(__('dsplus_fillin'));
			} else if (!__('dsplus_fillin')) {
				var fillInButton = _ce('input', '', {
					'id': 'dsplus_fillin',
					'type': 'button',
					'class': 'btn',
					'value': texts.gui.fillIn,
					'style': 'padding: 3px 10px;'
				});
				var trainFormTable = _T(__('train_form'), 'table', 0);
				if (!trainFormTable)
					return;
				var trainRows = trainFormTable.rows;
				var lastRow = trainRows[trainRows.length-1];
				if (lastRow.cells.length) {
					var fillCell = lastRow.cells[0];
					fillCell.style.textAlign = 'right';
					fillCell.appendChild(fillInButton);
					fillInButton.addEventListener('click', function() {
						var toBuild = {};
						var resAvailable = {
							wood: parseInt(__('wood').innerHTML, 10),
							stone: parseInt(__('stone').innerHTML, 10),
							iron: parseInt(__('iron').innerHTML, 10),
							pop: parseInt(__('pop_max_label').innerHTML, 10) - parseInt(__('pop_current_label').innerHTML, 10)
						};
						var trainRows = _T(__('train_form'), 'tr');
						for (var i = 1, row; row = trainRows[i]; i++) {
							var unitImg = _T(row, 'img', 0);
							var unitInput = _T(row, 'input', 'last');
							if (row.cells.length < 2 || !unitImg || !unitImg.src || !unitInput)
								continue;

							var match = unitImg.src.match(regex.unitImgPath);
							var unit = match[1];
							var max_possible = _T(row, 'a', 'last').innerHTML;
							var max_count = Template.getSelectedTemplate('unit', null, unit);
							if (Template.sum[unit][2] >= max_count || (max_possible == '(0)' && !parseInt(unitInput.value, 10)))
								continue;
							toBuild[unit] = max_count - Template.sum[unit][2];
						}
						toBuild = Template.getBuildUnits(resAvailable, toBuild);
						for (var unit in toBuild) {
							if (toBuild[unit] > 0) {
								var input = __(unit +'_0');
								input.value = toBuild[unit];
								lib.fireEvent(input, 'change');
							}
						}
					}, true);
				}
			}
		},
		getBuildUnits: function(resAvailable, toBuild) {
			var resNeeded = { wood: 0, stone: 0, iron: 0, pop: 0 };
			var minFactor = 1;
			for (var res in resNeeded) {
				for (var unit in toBuild)
					resNeeded[res] += lib.unitInfo[unit][res] * toBuild[unit];
				var factor = resAvailable[res] / resNeeded[res];
				if (factor < minFactor)
					minFactor = factor;
			}
			var count = 0;
			var toBuild2 = {};
			for (var unit in toBuild) {
				var c = Math.floor(toBuild[unit] * minFactor);
				toBuild2[unit] = toBuild[unit] - c;
				toBuild[unit] = c;
				count += toBuild[unit];
				for (var res in resNeeded)
					resAvailable[res] -= toBuild[unit] * lib.unitInfo[unit][res];
			}
			if (count) {
				var rest = true;
				for (var res in resNeeded)
					if (!resAvailable[res])
						rest = false;
				if (rest) {
					var toBuild2 = Template.getBuildUnits(resAvailable, toBuild2);
					for (var unit in toBuild)
						toBuild[unit] += toBuild2[unit];
				}
			}

			return toBuild;
		},
		unitInputKeyUp: function() {
			if (regex.NaN.test(this.value)) {
				var pos = getCursorPosition(this);
				var count = this.value.substr(0, pos).match(/[^\d]/g).length;
				this.value = this.value.replace(/[^\d]/g, '');
				setCursorPosition(this, pos-count);
			}

			Template.calcPop('unit', this.parentNode.parentNode);
		},
		unitInputChange: function() {
			if (!this.value)
				this.value = 0;
		},
		unitInputClick: function() {
			if (!this.value)
				this.value = 0;
			if (this.value == 0) {
				this.focus();
				this.select();
			}
		},
		unitInputBlur: function() {
			if (!this.value)
				this.value = 0;
			Template.unitInputKeyUp.call(this);
		},
		updateUnitSum: function() {
			var trainRows = _T(__('train_form'), 'tr');
			if (!trainRows.length)
				return;
			var colHead = _prev(trainRows[0].cells[trainRows[0].cells.length-1]);
			colHead.innerHTML = texts.gui.unitsTitle;

			Template.sum = {};
			for (var key in lib.unitInfo) {
				Template.sum[key] = [0, 0, 0];

				if (Queues.units[key])
					Template.sum[key][2] += Queues.units[key].num;
			}
			for (var i = 1, row; row = trainRows[i]; i++) {
				var img = _T(row, 'img', 0);
				var match = img && img.src && img.src.match(regex.unitImgPath);
				if (!match)
					continue;
				var key = match[1];
				var numberCell = row.cells[row.cells.length-2];

				var val = numberCell.innerHTML.split('/');
				Template.sum[key][0] = parseInt(val[0], 10);
				Template.sum[key][1] = parseInt(val[1], 10);
				Template.sum[key][2] += Template.sum[key][1];

				numberCell.innerHTML = Template.sum[key].join('/');
			}
		},
		updateUnitLinks: function() {
			// no template selected; show all links
			var trainRows = _T(__('train_form'), 'tr');
			if (Template.getSelectedIndex('unit') < 0) {
				for (var i = 1, row; row = trainRows[i]; i++) {
					var img = _T(row, 'img', 0);
					if (row.cells.length > 1 && img && img.src) {
						var content = row.cells[row.cells.length-1].children;
						for (var j = 0, con; con = content[j]; j++) {
							_show(con);
							if (con.innerHTML.indexOf(texts.gui.troopsNumReached) > -1)
								_hide(con);
						}
					}
				}
				return;
			}

			// TODO: number of free popuplation - number of snobs * 100 - number of the reserved free popuplation
			var activeElement = document.activeElement;
			for (var i = 1, row; row = trainRows[i]; i++) {
				var img = _T(row, 'img', 0);
				if (row.cells.length < 2 || !img || !img.src)
					continue;
				var match = img.src.match(regex.unitImgPath);
				if (!match)
					continue;
				var unit = match[1];
				var max_count = Template.getSelectedTemplate('unit', null, unit);
				var lastCell = row.cells[row.cells.length-1];

				if (Template.sum[unit][2] >= max_count) {
					// if there are elements (input + links etc.)
					if (lastCell.children.length) {
						// if there is also an element -> show it and hide the other elements
						var insertElement = true;
						var children = lastCell.children;
						for (var j = 0, child; child = children[j]; j++) {
							if (child.tagName == 'SPAN' && child.innerHTML.indexOf(texts.gui.troopsNumReached) > -1) {
								_show(child);
								insertElement = false;
							} else {
								if (child.tagName == 'INPUT' && child == activeElement) {
									child.blur();
								}
								_hide(child);
							}
						}
						// if not -> insert one
						if (insertElement) {
							var span = _ce('span', texts.gui.troopsNumReached, { 'class': 'inactive' });
							lastCell.appendChild(span);
							lastCell.style.minWidth = '160px';
						}
					}
					// too low resources or not explored or not enough free population etc.
					else {
						lastCell.innerHTML = texts.gui.troopsNumReached;
						lastCell.style.minWidth = '160px';
					}
				} else {
					for (var j = 0, child; child = lastCell.children[j]; j++) {
						if (child.tagName == 'SPAN' && child.innerHTML.indexOf(texts.gui.troopsNumReached) > -1)
							_hide(child);
						else
							_show(child);
					}
					// the _0_a is composed this way: '_' + village_id + '_a'
					//var link = __(unit +'_0_a'); => becomes win.unit_build_block.get_a(unit) for mass recruitment
					var link = win.unit_build_block.get_a(unit);
					if (link) {
						var input = _T(link.parentNode, 'input', 0);
						var input_value = parseInt(input.value, 10) || 0;
						var max_build = parseInt(link.innerHTML.replace(/[\(\)]/g, ''), 10) + input_value;
						if (max_build + Template.sum[unit][2] > max_count) {
							var num = max_count - Template.sum[unit][2] - input_value;
							if (num < 0)
								input.style.color = 'red';
							num = Math.max(num, 0);
							link.innerHTML = '('+ num +')';
							if (!link.classList.contains('monitored')) {
								link.addEventListener('click', function() {
									var unit = this.id.replace(/_\d+_a/, '');
									var max_count = Template.getSelectedTemplate('unit', null, unit);
									var max_possible = parseInt(this.innerHTML.match(/\((\d+)\)/)[1], 10);
									var num = Math.max(max_count - Template.sum[unit][2], 0);
									if (num > max_possible)
										num = max_possible;
									if (num === 0)
										num = '';
									__(unit +'_0').value = num;
									win.unit_build_block._onchange();

									Template.updateUnitLinks();
								}, false);
								link.classList.add('monitored');
							}
							link.href = 'javascript:;';
						}
						if (!input.classList.contains('monitored')) {
							input.addEventListener('change', function() {
								Template.updateUnitLinks();
							}, false);
							input.classList.add('monitored');
						}
					}
				}
			}
		},



		/*
		 * stuff section
		 */
		insertTemplateSelect: function(type, selectCell) {
			if (!__(type +'_templateselect')) {
				selectCell.classList.add('nowrap');
				Template.createTemplateSelect(type, selectCell, true);

				var editLink = _T(selectCell, 'a', 'last');
				editLink.addEventListener('click', function() {
					Template.clickEditTemplate(type);
				}, true);
				return true;
			}
			return false;
		},
		clickEditTemplate: function(type) {
			var templateHeader = texts.gui[type +'TemplateHeader'];
			var addText = (type == 'unit' ? texts.gui.addUnitTemplate : texts.gui.addBuildTemplate);
			var typeCols = type +'Cols';
			var HTML = '<h5 style="text-align: center;">'+ templateHeader +'</h5>' +
			'<table class="vis" style="width: 100%;"><tbody id="dsplus_'+ type +'_templates"><tr>';
			for (var i = 0, max = Template[typeCols].order.length; i < max; i++) {
				var key = Template[typeCols].order[i];
				HTML += '<th '+ Template[typeCols][key].cellAttr +'>'+ Template[typeCols][key].head +'</th>';
			}
			HTML += '</tr>';

			for (var i = 0, max = Data.templates.get(type +'_templates').length; i < max; i++) {
				HTML += '<tr id="'+ type +'_'+ i +'">'+ Template.createTemplateRow(type, i) +'</tr>';
			}
			HTML += '</tbody></table>' +
			'<div style="text-align: center;"><a href="javascript:;">'+ addText +'</a></div>';
			var pos, width;
			var attachEvents = !__(type +'_templates');
			if (type == 'unit') {
				var $secondRow = jQuery(_T(__('train_form'), 'tr', 1));
				var $inlinePopup = jQuery(__('inline_popup'));

				pos = $secondRow.offset();
				width = $secondRow.outerWidth() - $inlinePopup.outerWidth() + $inlinePopup.width();
			} else if (type == 'build') {
				var $firstRow = jQuery(__('buildings').rows[0]);

				pos = $firstRow.offset();
				pos.top += $firstRow.outerHeight() + 2;
				width = $firstRow.outerWidth() - 4;
			}
			lib.inlinePopup(type +'_templates', pos.left, pos.top, HTML, true, width, 'auto');
			recreateToolTip();

			if (attachEvents) {
				var rows = __('dsplus_'+ type +'_templates').rows;
				Template.attachRowEvents(type, _slice(rows, 1));

				// add template
				var addLink = _T(__(type +'_templates'), 'a', 'last');
				addLink.addEventListener('click', function() {
					var newRow = _ce('tr', Template.createTemplateRow(type, null, true));
					__('dsplus_'+ type +'_templates').appendChild(newRow);
					Template.attachRowEvents(type, newRow);
				}, false);
			}
		},
		createTemplateSelect: function(type, element, edit, selected, noID, villageID) {
			if (typeof(villageID) == 'undefined')
				villageID = game_data.village.id;
			if (typeof(selected) != 'number')
				selected = Data.templates.getSelected(type, villageID);

			var options = '<option'+ (selected == -1 ? ' selected' : '') +'>('+ texts.gui.stdSelect +')</option>';
			for (var i = 0, max = Data.templates.get(type +'_templates').length; i < max; i++) {
				options += '<option'+ (selected == i ? ' selected' : '') +'>'+ Template.getTemplateName(type, i) +'</option>';
			}
			var select = _ce('select', options, {
				size: 1,
				'class': type +' template_select village_'+ villageID +' '+ type +'_templateselect',
				id: (noID ? '' : type +'_templateselect')
			});
			var editLink = _text('');
			var editHTML = '';
			if (edit) {
				editLink = _ce('a', '<img alt="#" src="graphic/rename.png">', {
					'class': 'edit-icon',
					href: 'javascript:;'
				});
				editHTML = ' '+ editLink.outerHTML;
			}
			if (element) {
				element.appendChild(_text(' '));
				element.appendChild(select);
				element.appendChild(_text(' '));
				element.appendChild(editLink);
				select.addEventListener('change', Template.changeTemplate, true);
			}
			return ' '+ select.outerHTML + editHTML;
		},
		changeTemplate: function() {
			var cls = this.className || '';
			var type = cls.split(' ')[0];
			if (!Data.templates.isType(type))
				return;

			var match = cls.match(/village_(\d+)/);
			var villageID = match ? match[1] : game_data.village.id;
			Data.templates.set(type +'_selected', villageID, this.selectedIndex-1);
			setTimeout(function() {
				Data.templates.save();
			});

			if (type == 'unit') {
				Template.updateUnitLinks();
				Template.createFillInButton();
			} else if (type == 'build') {
				if (__('buildings')) {
					Template.updateBuildLinks();
				} else {
					Template.updateBuildOverviewLinks();
				}
			}
		},
		createTemplateRow: function(type, template, edit) {
			var kind = 'cell'+ (edit ? 'Edit' : 'Show');
			var otherType = {
				build: 'unit',
				unit: 'build'
			}[type];
			var HTML = '';
			if (typeof(template) == 'string' || typeof(template) == 'number')
				template = Data.templates.get(type +'_templates', template);

			for (var i = 0, max = Template[type].order.length; i < max; i++) {
				var key = Template[type].order[i];
				var val = template ? template[key] : Template[type][key]['default'];
				if (val === undefined)
					val = Template[type][key]['default'];
				var cont = Template[type][key][kind];
				HTML += '<td ' + Template[type][key].cellAttr + '>';
				if (/%template_name%/.test(cont))
					HTML += cont.replace(/%template_name%/g, Template.getTemplateName(otherType, val));
				else if (/%template_input%/.test(cont))
					HTML += cont.replace(/%template_input%/g, Template.createTemplateSelect(otherType, null, false, val, true));
				else {
					if (edit) {
						HTML += cont.replace(/%val%/g, val);
					} else {
						if (Template[type][key].grey && (!val || val == '0'))
							HTML += cont.replace(/%val%/g, '<span class="grey">0</span>');
						else
							HTML += cont.replace(/%val%/g, val);
					}
				}
				HTML += '</td>';
			}
			return HTML;
		},
		attachRowEvents: function(type, row) {
			if (is_array(row)) {
				for (var i = 0, len = row.length; i < len; i++)
					Template.attachRowEvents(type, row[i]);
				return;
			}
			var inputs = _T(row, 'input');
			var links = _T(row, 'a');
			if (type == 'unit') {
				for (var i = 2, input; input = inputs[i]; i++) {
					if (input.type != 'text')
						continue;
					input.addEventListener('keyup', Template.unitInputKeyUp, false);
					input.addEventListener('change', Template.unitInpuChange, false);
					input.addEventListener('click', Template.unitInputClick, false);
					input.addEventListener('blur', Template.unitInputBlur, false);
				}

				// edit
				for (var i = 0, link; link = links[i]; i++) {
					if (link.classList.contains('edit-icon'))
						link.addEventListener('click', Template.editTemplate, true);
				}
			} else if (type == 'build') {
				for (var i = 2, input; input = inputs[i]; i++) {
					if (input.type != 'text')
						continue;
					input.addEventListener('keyup', Template.buildInputKeyUp, false);
					input.addEventListener('change', Template.buildInpuChange, false);
					input.addEventListener('click', Template.buildInputClick, false);
					input.addEventListener('blur', Template.buildInputBlur, false);
				}

				// edit (attach for each first 'a.edit-icon'-element)
				for (var i = 0, link; link = links[i]; i++) {
					if (link.classList.contains('edit-icon')) {
						link.addEventListener('click', function() {
							/*var row;
							if (lib.buildingInfo.hasOwnProperty('church'))
								row = this.parentNode.parentNode;*/
							Template.editTemplate.call(this);

							if (lib.buildingInfo.church) {
								var church, church_f;
								for (var i = 0, input; input = inputs[i]; i++) {
									if (input.classList.contains('dsplus_church'))
										church = input;
									else if (input.classList.contains('dsplus_church_f'))
										church_f = input;
								}
								if (church.value && church.value != '0' && church_f.value && church_f.value != '0') {
									church.style.color = 'red';
									church_f.style.color = 'red';
								} else if (church_f.value && church_f.value != '0')
									church.disabled = true;
								else if (church.value && church.value != '0')
									church_f.disabled = true;
							}
						}, true);
					}
				}
			}

			// save
			for (var i = 0, input; input = inputs[i]; i++) {
				if (input.type == 'button')
					input.addEventListener('click', Template.clickOK, true);
			}

			// delete
			for (var i = 0, link; link = links[i]; i++) {
				if (link.classList.contains('cancel-icon'))
					link.addEventListener('click', Template.deleteTemplate, true);
			}
		},
		saveTemplate: function(type, template, num) {
			var templateSelectList = _C(doc, type +'_templateselect');
			if (typeof(num) == 'undefined') {
				for (var i = 0, templateSelect; templateSelect = templateSelectList[i]; i++) {
					var newOption = _ce('option', template.name);
					templateSelect.appendChild(newOption);
				}

				num = Data.templates.addTemplate(type, template);
				Data.templates.save();
			} else {
				var templateSelectList = _C(doc, type +'_templateselect');
				for (var i = 0, templateSelect; templateSelect = templateSelectList[i]; i++) {
					var option = _T(templateSelect, 'option', parseInt(num, 10)+1);
					if (!option)
						continue;
					option.innerHTML = template.name;
				}

				Data.templates.set(type +'_templates', num, template);
				Data.templates.save();
			}

			if (type == 'unit') {
				Template.doUnitTemplate();
			} else {
				if (__('buildings')) {
					Template.doBuildTemplate();
				} else {
					Template.doBuildMassTemplate();
				}
			}

			return num;
		},
		deleteTemplate: function() {
			var row = this.parentNode.parentNode;
			if (row.id) {
				var match = row.id.match(/(.+)_(\d+)/);
				if (match) {
					var type = match[1];
					var num = parseInt(match[2], 10);
					Data.templates.del(type, num);

					var templateSelectList = _C(doc, type +'_templateselect');
					for (var i = 0, templateSelect; templateSelect = templateSelectList[i]; i++) {
						var options = templateSelect.children;
						if (templateSelect.selectedIndex-1 == num) {
							options[0].selected = true;
							templateSelect.selectedIndex = 0;
						}
						var option = _select(options, num+1);
						_remove(option);
					}
				}
			}
			_remove(row);
		},
		getTemplateName: function(type, num) {
			if (num == -1) return '<span class="grey">-</span>';
			return Data.templates.get(type +'_templates', num).name;
		},
		getSelectedIndex: function(type, villageID) {
			villageID = villageID || game_data.village.id;
			return Data.templates.getSelected(type, villageID);
		},
		getSelectedTemplate: function(type, villageID, key) {
			var selIdx = Template.getSelectedIndex(type, villageID);
			if (selIdx < 0) return 999999;
			var template = Data.templates.get(type +'_templates', selIdx);
			if (key) return template[key];
			return template;
		},
		calcPop: function(type, row) {
			var pop = 0;
			if (type == 'unit') {
				for (var i = 0, max = Template.unit.order.length; i < max; i++) {
					var key = Template.unit.order[i];
					var input = _C(row, 'dsplus_'+ key, 0) || {};
					var val = parseInt(input.value, 10);
					if (isNaN(val)) {
						val = 0;
					}
					if (key == 'free_pop' || lib.unitInfo[key]) {
						pop += (key == 'free_pop' ? 1 : lib.unitInfo[key].pop) * val;
					}
				}
				var input = _C(row, 'dsplus_pop', 0);
				if (input)
					input.innerHTML = pop;
				return pop;
			} else if (type == 'build') {
				for (var i = 0, max = Template.build.order.length; i < max; i++) {
					var key = Template.build.order[i];
					if (Template.build[key].factorIntoPop) {
						var input = _C(row, 'dsplus_'+ key, 0) || {};
						var val = parseInt(input.value, 10);
						if (!isNaN(val) && val > 0) {
							pop += Math.round( Math.pow(lib.buildingInfo[key].pop_factor, val - 1) * lib.buildingInfo[key].pop );
						}
					}
				}
				input = _C(row, 'dsplus_pop', 0);
				if (input)
					input.innerHTML = pop;
				return pop;
			}
		},
		calcPoints: function(row) {
			var points = 0;
			for (var key in lib.buildingInfo) {
				var input = _C(row, 'dsplus_'+ key, 0) || {};
				var val = input.value;
				if (!val || val == '0')
					continue;
				points += Math.round(lib.buildingPoints[key] * Math.pow(1.2, parseInt(val, 10) - 1));
			}
			var input = _C(row, 'dsplus_points', 0);
			if (input)
				input.innerHTML = points;
			return points;
		},
		calcRes: function(build, level) {
			if (!lib.buildingInfo[build])
				return {
					wood: 0,
					stone: 0,
					iron: 0,
					pop: 0,
					popDiff: 0
				};

			var wood = Math.round( lib.buildingInfo[build].wood * Math.pow(lib.buildingInfo[build].wood_factor, level) );
			var stone = Math.round( lib.buildingInfo[build].stone * Math.pow(lib.buildingInfo[build].stone_factor, level) );
			var iron = Math.round( lib.buildingInfo[build].iron * Math.pow(lib.buildingInfo[build].iron_factor, level) );
			var pop = Math.round( lib.buildingInfo[build].pop * Math.pow(lib.buildingInfo[build].pop_factor, level) );
			var lastPop = Math.round( lib.buildingInfo[build].pop * Math.pow(lib.buildingInfo[build].pop_factor, level-1) );
			return {
				wood: wood,
				stone: stone,
				iron: iron,
				pop: pop,
				popDiff: pop - lastPop
			};
		},
		editTemplate: function() {
			var row = this.parentNode;
			while (row && row.tagName != 'TR')
				row = row.parentNode;

			var match = row.id && row.id.match(/(.+)_(\d+)/);
			if (!match) return;
			var type = match[1];
			var num = match[2];

			row.innerHTML = Template.createTemplateRow(type, num, true);

			Template.attachRowEvents(type, row);
		},
		clickOK: function() {
			var row = this.parentNode.parentNode;
			var tBody = row.parentNode;
			var type = tBody && tBody.id.match(/^dsplus_(.+)_templates$/)[1];
			var typeCol = type +'Cols';
			var num = row.id && row.id.match(/.+_(\d+)/)[1];
			var nameInput = _C(row, 'dsplus_name', 0);
			var obj = {
				name: nameInput.value
			};
			if (!obj.name) {
				lib.alert(texts.gui.emptyNameError);
			} else {
				for (var i = 1, max = Template[typeCol].order.length; i < max; i++) {
					var key = Template[typeCol].order[i];
					var el = _C(row, 'dsplus_'+ key, 0);
					if (!el)
						continue;

					// is used by the template-select (is currently commented out)
					if (regex.template.test(key)) {
						var select = _T(el, 'select', 0);
						if (select)
							obj[key] = select.selectedIndex-1;
					} else {
						var val = Template[typeCol][key]['default'];
						if (el.tagName == 'INPUT') {
							if (!el.value)
								el.value = 0;
							val = el.value;
						} else
							val = el.innerHTML;
						// conversion
						if (typeof(Template[typeCol][key]['default']) == 'number')
							val = parseInt(val, 10);
						obj[key] = val;
					}
				}
				row.innerHTML = Template.createTemplateRow(type, obj);
				Template.attachRowEvents(type, row);

				if (!num)
					row.id = type +'_'+ Template.saveTemplate(type, obj);
				else
					Template.saveTemplate(type, obj, num);
			}
		}
	};


	var Infos = {
		villageID: null,
		addResourceRow: function(infoTable, ID) {
			var res = Data.villages.getRes(ID, null, false);
			var resWarn = Data.villages.getResWarn(ID, null, false);
			var resWarn90 = Data.villages.getResWarn(ID, null, false, 0.9);
			var resProd = Data.villages.getResProd(ID);
			var storageColorActive = Data.settings.get('storageColor_enabledAt', screen);

			var row = infoTable.insertRow(-1);

			var cell = row.insertCell(-1);
			cell.className = 'nowrap';
			cell.innerHTML = texts.gui.contingentRes +':';

			var cell = row.insertCell(-1);
			cell.style.padding = 0;

			var tab = _ce('table');
			tab.className = 'overview_table';
			tab.setAttribute('style', 'border-spacing: 6px 0;');
			cell.appendChild(tab);

			var tabRow = tab.insertRow(-1);

			var woodCell = tabRow.insertCell(-1);
			woodCell.style.padding = '2px 3px';
			var woodClass = 'res wood';
			if (storageColorActive) {
				var woodColor = Storage.getColor(res.wood, res.max);
				woodCell.style.backgroundColor = woodColor.toString();
				woodCell.style.color = woodColor.getContrastColor().toString();
			} else {
				woodClass = (resWarn.wood ? 'warn' : (resWarn90.wood ? 'warn_90' : 'res')) +' wood';
			}
			woodCell.innerHTML = '<span class="'+ woodClass +'" title="'+ resProd.wood +'">'+ lib.formatNumber(res.wood, true, true) +'</span>';

			var stoneCell = tabRow.insertCell(-1);
			stoneCell.style.padding = '2px 3px';
			var stoneClass = 'res stone';
			if (storageColorActive) {
				var stoneColor = Storage.getColor(res.stone, res.max);
				stoneCell.style.backgroundColor = stoneColor.toString();
				stoneCell.style.color = stoneColor.getContrastColor().toString();
			} else {
				stoneClass = (resWarn.stone ? 'warn' : (resWarn90.stone ? 'warn_90' : 'res')) +' stone';
			}
			stoneCell.innerHTML = '<span class="'+ stoneClass +'" title="'+ resProd.stone +'">'+ lib.formatNumber(res.stone, true, true) +'</span>';

			var ironCell = tabRow.insertCell(-1);
			ironCell.style.padding = '2px 3px';
			var ironClass = 'res iron';
			if (storageColorActive) {
				var ironColor = Storage.getColor(res.iron, res.max);
				ironCell.style.backgroundColor = ironColor.toString();
				ironCell.style.color = ironColor.getContrastColor().toString();
			} else {
				ironClass = (resWarn.iron ? 'warn' : (resWarn90.iron ? 'warn_90' : 'res')) +' iron';
			}
			ironCell.innerHTML = '<span class="'+ ironClass +'" title="'+ resProd.iron +'">'+ lib.formatNumber(res.iron, true, true) +'</span>';

			var sumCell = tabRow.insertCell(-1);
			sumCell.style.padding = '2px 3px';
			sumCell.innerHTML = '<span style="padding: 0 10px 0 0;">/</span><span style="background: transparent url(\''+ image_base +'res.png\') no-repeat;" class="res" title="'+ texts.gui.effectiveStorage +'">'+ lib.formatNumber(res.max, true, true) +'</span>';

			return row;
		},
		addMoodRow: function(infoTable, ID) {
			var loyalty = Data.villages.getMood(ID);
			if (loyalty < 100) {
				var row = infoTable.insertRow(-1);

				var cell = row.insertCell(-1);
				cell.className = 'nowrap';
				cell.innerHTML = texts.gui.mood +':';

				var cell = row.insertCell(-1);
				cell.innerHTML = loyalty;

				return row;
			}
		},
		addBuildingRow: function(infoTable, ID, pointsDiff) {
			if (!Data.villages.hasBuildings(ID))
				return;

			Style.add('bildings_info').set(
				'.dsplus_building_upgrade { color: gray; font-size: 80%; cursor: pointer; -moz-user-select: none; -webkit-user-select: none; }'
			);

			var row = infoTable.insertRow(-1);

			var cell = row.insertCell(-1);
			cell.className = 'nowrap';
			cell.innerHTML = texts.gui.buildings +':<br><span style="font-size: 80%; font-style: italic;">'+ Data.villages.getSpyDate(ID, texts.locale.dateFormat) +'</span>';

			var cell = row.insertCell(-1);

			var tab = _ce('table', '', {
				'id': 'buildings'
			});
			var row1 = tab.insertRow(-1);
			var row2 = tab.insertRow(-1);
			var row3 = tab.insertRow(-1);
			cell.appendChild(tab);

			var i = 0;
			// TODO: getBuildUps kommt wo anders rein!
			var buildingConstruction = pointsDiff ? Village.getBuildUps(ID, pointsDiff) : {};
			for (var build in lib.buildingInfo) {
				var buildingLevel = Data.villages.getBuilding(ID, build);
				var newLevel = buildingConstruction[build] || 0;
				if (buildingLevel > 0 || newLevel > 0) {
					var cell1 = row1.insertCell(-1);
					cell1.style.textAlign = 'center';
					cell1.style.width = '25px';
					if (i) cell1.style.borderLeft = '1px dashed #C1A264;';
					var buildName = texts.building[build] || '';
					cell1.innerHTML = '<img src="'+ lib.buildingImgPath.replace('%building%', build) +'" title="'+ buildName +'" alt="">';

					var cell2 = row2.insertCell(-1);
					cell2.style.textAlign = 'center';
					if (i) cell2.style.borderLeft = '1px dashed #C1A264';
					if (build == 'wall' && buildingLevel > 4) {
						cell2.innerHTML = '<span class="'+ (buildingLevel > 9 ? 'warn' : 'warn_90') +'">'+ buildingLevel +'</span>';
					} else {
						cell2.innerHTML = buildingLevel;
					}

					var cell3 = row3.insertCell(-1);
					cell3.style.textAlign = 'center';
					if (i) cell3.style.borderLeft = '1px dashed #C1A264';
					if (newLevel) cell3.innerHTML = '<span class="dsplus_building_upgrade dsplus_building_'+ build +'_'+ i +'">('+ newLevel +')</span>';

					i++;
				}
			}

			return row;
		},
		addUnitsRow: function(infoTable, ID, specialEvent) {
			var row = infoTable.insertRow(-1);

			var cell = row.insertCell(-1);
			cell.className = 'nowrap';
			cell.innerHTML = texts.gui.units +':<br><span style="font-size: 80%; font-style: italic;">'+ Data.villages.getLastDate(ID, texts.locale.dateFormat) +'</span>';

			var cell = row.insertCell(-1);

			var tab = _ce('table', '', {
				'id': 'units'
			});
			var row1 = tab.insertRow(-1);
			var row2 = tab.insertRow(-1);
			cell.appendChild(tab);

			var i = 0;
			var unitObj = specialEvent ? Data.villages.getSpecialUnits(ID) : lib.unitInfo;
			for (var key in unitObj) {
				var cell1 = row1.insertCell(-1);
				cell1.style.textAlign = 'center';
				cell1.style.width = '25px';
				if (i) cell1.style.borderLeft = '1px dashed #C1A264';
				var unitName = texts.unit[key] || '';
				cell1.innerHTML = '<img src="'+ lib.unitImgPath.replace('%unit%', key) +'" title="'+ unitName +'" alt="">';

				var cell2 = row2.insertCell(-1);
				cell2.style.textAlign = 'center';
				if (i) cell2.style.borderLeft = '1px dashed #C1A264';
				cell2.className = 'unit-item';
				var cls = 'unit-item';
				var num = Data.villages.getUnit(ID, key);
				if (num < 1) {
					cls += ' hidden';
					if (num < 0) num = '?';
				}
				cell2.className = cls;
				cell2.innerHTML = num;

				i++;
			}

			return row;
		},
		getRuntimesTable: function(coordDiff) {
			var table = _ce('table');
			var row1 = table.insertRow(-1);
			row1.className = 'center';
			var row2 = table.insertRow(-1);
			row2.className = 'center';

			var i = 0;
			for (var unit in lib.unitInfo) {
				var uSpeed = lib.unitInfo[unit].speed;
				if (uSpeed > 1) {
					var color = (i % 2) ? '#F8F4E8' : '#DED3B9';

					var cell1 = row1.insertCell(-1);
					cell1.style.padding = '2px';
					cell1.style.backgroundColor = color;
					cell1.innerHTML = '<img src="'+ lib.unitImgPath.replace('%unit%', unit) +'" title="'+ texts.unit[unit] +'">';

					var cell2 = row2.insertCell(-1);
					cell2.style.backgroundColor = color;
					cell2.innerHTML = Infos.formatRuntime(coordDiff * uSpeed);
				}
				i++;
			}
			return table;
		},
		formatRuntime: function(time) {
			var date = {
				H: 0,
				m: 0,
				s: 0
			};
			date.H = Math.floor(time / 60);
			time -= date.H * 60;
			date.m = Math.floor(time);
			time -= date.m;
			date.s = Math.round(time * 60);
			if (date.s >= 60) {
				date.m++;
				date.s -= 60;
			}
			if (date.m >= 60) {
				date.H++;
				date.m -= 60;
			}

			var formatStr = texts.locale.runtime;
			var ret = '';
			for (var i = 0; i < formatStr.length; i++) {
				var str = formatStr[i];
				if (date.hasOwnProperty(str)) {
					if (str == formatStr[i+1]) {
						if (date[str] < 10) ret += '0';
						i++;
					}
					ret += date[str];
				} else {
					ret += str;
				}
			}
			return ret;
		}
	};


	var Farmlist = {
		sortKey: Data.get('FarmSortKey'),
		sortDir: Data.get('FarmSortDir'),
		normalList: [],
		attackList: [],
		sortCol: function(el, e) {
			var match = e.target.id.match(/FarmCol-(.+)-(.+)/);
			var key = match[1];
			var dir = (match[2] == 'up' ? 1 : -1);
			if (Farmlist.sortKey != key || Farmlist.sortDir != dir) {
				Farmlist.sortKey = key;
				Farmlist.sortDir = dir;
				setTimeout(function() {
					Data.set('FarmSortKey', key);
					Data.set('FarmSortDir', dir);
					Data.save();
				});

				_remove(__('dsplus_farmTable'));
				Farmlist.load();
			}
		},
		changeCoords: function() {
			// change the currently selected FarmRow
			var selectedFarmRow = __('selectedFarmRow');
			if (selectedFarmRow)
				selectedFarmRow.id = '';

			var inputx = __('inputx');
			var inputy = __('inputy');
			if (!inputx || !inputy || regex.NaN.test(inputx.value + inputy.value))
				return;

			var markedRow;
			var coord = inputx.value +'|'+ inputy.value;
			var links = _T(__('dsplus_farmTable'), 'a');
			for (var i = 0, link; (link = links[i]) && !markedRow; i++) {
				if (link.innerHTML == coord)
					markedRow = link.parentNode.parentNode;
			}
			if (markedRow)
				markedRow.id = 'selectedFarmRow';
		},
		// send units
		clickCoords: function(coords) {
			var ID = Data.villages.getID(coords);
			var coords = coords.split('|');
			win.selectTarget(coords[0], coords[1], '');
			Farmlist.changeCoords();

			var usableUnits = getUsableUnits();

			// if there are no units ready to send which can carry resources
			insertFarmTroops(ID, usableUnits);

			Farmlist.scrollToUnitsForm();
		},
		// spy only
		clickAge: function(coords) {
			var coords = coords && coords.split('|');
			var ID = Data.villages.getID(coords);
			var farmSpysSettings = Data.settings.get('farming_farmSpys');
			var farmSpys = farmSpysSettings[1];
			var unit_input_spy = __('unit_input_spy');
			var match = _next(unit_input_spy).innerHTML.match(/\((\d+)\)/);
			var availableSpys = (match && match[1]) || 0;
			if (coords)
				win.selectTarget(coords[0], coords[1], '');
			Farmlist.changeCoords();

			if (ID && !Data.villages.isBarbarian(ID))
				farmSpys = Math.max(Data.get('spynum_player'), farmSpys);
			win.selectAllUnits(false);
			win.insertUnit(jQuery(unit_input_spy), Math.min(farmSpys, availableSpys));

			Farmlist.scrollToUnitsForm();
		},
		scrollToUnitsForm: function() {
			var $units_form = jQuery(__('units_form'));
			var $topContainer = jQuery(__('topContainer'));
			var $doc = jQuery(document);
			var top1 = $units_form.offset().top - $topContainer.height();
			var top2 = $units_form.parent().offset().top - $topContainer.height();

			if ($doc.scrollTop() > top1)
				$doc.scrollTop(top2);
		},
		lockFarm: function(el, e) {
			el.blur();
			var row = el.parentNode;
			while (row && row.tagName != 'TR')
				row = row.parentNode;

			var farmLink = _C(row, 'farm_link', 0);
			var match = farmLink.innerHTML.match(/(\d+\|\d+)/);
			var coords = match[1];
			setTimeout(function() {
				var ID = Data.villages.getID(coords);
				Data.villages.setLocked(ID, true);
				Data.villages.save();
			});
			_remove(row);
		},
		delFarm: function(el, e) {
			el.blur();
			function delIt() {
				var row = el.parentNode;
				while (row && row.tagName != 'TR')
					row = row.parentNode;

				var farmLink = _C(row, 'farm_link', 0);
				var match = farmLink.innerHTML.match(/(\d+\|\d+)/);
				var coords = match[1];
				setTimeout(function() {
					var ID = Data.villages.getID(coords);
					Data.villages.delData(ID);
				});
				_remove(row);
			}
			if (!Data.settings.get('confirm_deleting')) {
				delIt();
			} else {
				var buttons = [{
					text: texts.gui.OK,
					callback: delIt,
					confirm: true
				}];
				UI.ConfirmationBox(texts.gui.delConfirm, buttons);
			}
		},

		colOrder: ['attack', 'coords', 'wood', 'stone', 'iron', 'sum', 'eq', 'unitsInfo', 'distance', 'wall', 'age', 'lock', 'delete'],
		cols: {
			attack: {
				headAttr: 'style="width: 12px;"',
				head: '',
				sortable: false,
				cellStyle: 'display: none;'
			},
			coords: {
				headAttr: 'id="dsplus_coords"',
				head: '<span style="color:transparent">'+ texts.gui.x +'</span>'+ texts.gui.x +'<span style="color:transparent">'+ texts.gui.x +'</span>|' +
					'<span style="color:transparent">'+ texts.gui.y +'</span>'+ texts.gui.y +'<span style="color:transparent">'+ texts.gui.y +'</span> (<span id="dsplus_coords_count">?</span>)',
				sortable: false,
				cellStyle: 'display: none;'
			},
			wood: {
				headAttr: 'style="text-align: right;"',
				head: '<span class="icon header wood tooltip" title="'+ texts.gui.wood +'"> </span>',
				sortable: true,
				cellStyle: 'display: none;'
			},
			stone: {
				headAttr: 'style="text-align: right;"',
				head: '<span class="icon header stone tooltip" title="'+ texts.gui.stone +'"> </span>',
				sortable: true,
				cellStyle: 'display: none;'
			},
			iron: {
				headAttr: 'style="text-align: right;"',
				head: '<span class="icon header iron tooltip" title="'+ texts.gui.iron +'"> </span>',
				sortable: true,
				cellStyle: 'display: none;'
			},
			sum: {
				headAttr: 'style="text-align: right;"',
				head: '<span class="icon header ressources tooltip" title="'+ texts.gui.total +'"> </span>',
				sortable: true,
				cellStyle: 'display: none;'
			},
			eq: {
				headAttr: 'style="text-align: right;"',
				head: texts.gui.eq,
				sortable: true,
				cellStyle: 'display: none;'
			},
			unitsInfo: {
				headAttr: 'style="text-align: right;"',
				head: '<img src="'+ image_base +'command/attack.png" alt="" title="'+ texts.gui.noUnitsCol +'" class="tooltip">',
				sortable: false,
				cellStyle: 'display: none;'
			},
			distance: {
				headAttr: 'style="text-align: right;"',
				head: '<img src="'+ image_base +'unit/speed.png" alt="" title="'+ texts.gui.distance +'" class="tooltip">',
				sortable: true,
				cellStyle: 'display: none;'
			},
			wall: {
				headAttr: 'style="text-align: right;"',
				head: '<img src="'+ image_base +'buildings/wall.png" alt="" title="'+ texts.building.wall +'" class="tooltip">',
				sortable: true,
				cellStyle: 'display: none;'
			},
			age: {
				headAttr: 'style="text-align: right;"',
				head: '<span>'+ texts.gui.age +'</span>',
				sortable: true,
				cellStyle: 'display: none;'
			},
			lock: {
				headAttr: 'style="width: 15px;"',
				head: '',
				sortable: false,
				cellStyle: 'display: none;'
			},
			'delete': {
				headAttr: 'style="width: 15px;"',
				head: '',
				sortable: false,
				cellStyle: 'display: none;'
			}
		},
		load: function() {
			var pos = {};
			var HTML = '';
			var dsplus_farmlistLink = __('dsplus_farmlistLink');
			var $dsplus_farmlistLink = jQuery(__('dsplus_farmlistLink'));
			// farmlist is a popup
			if (Data.settings.get('farmlist_option') == 1) {
				HTML += '<h3>Farmen</h3>' +
					'<table class="vis" style="width: 100%;">';
				var $units_form = jQuery(__('units_form'));
				pos = {
					x: $units_form.offset().left,
					y: $dsplus_farmlistLink.offset().top + 18,
					width: Math.max($units_form.outerWidth(), 690),
					height: 340
				};
			}

			HTML += '<tbody id="dsplus_farmTable"><tr>';
			for (var i = 0, key; key = Farmlist.colOrder[i]; i++) {
				var val = Farmlist.cols[key];
				val.cellStyle = '';
				HTML += '<th '+ val.headAttr +'>'+ val.head;
				if (val.sortable) {
					HTML += ' <a id="FarmCol-'+ key +'-up" class="sortCol'+ (Farmlist.sortKey == key && Farmlist.sortDir == 1 ? '' : ' grey') +'" href="javascript:;">'+ lib.guiUp +'</a>' +
						'<a id="FarmCol-'+ key +'-down" class="sortCol'+ (Farmlist.sortKey == key && Farmlist.sortDir == -1 ? '' : ' grey') +'" href="javascript:;">'+ lib.guiDown +'</a>';
				}
				HTML += '</th>';
			}
			HTML += '</tr>' +
			'</tbody>';

			// farmlist is a popup
			if (Data.settings.get('farmlist_option') == 1) {
				HTML += '</table>';
				var farmlist = __('farmlist');
				if (farmlist)
					_remove(farmlist);
				lib.inlinePopup('farmlist', pos.x, pos.y, HTML, true, pos.width, pos.height);
			}
			// integrated farmlist
			else if (Data.settings.get('farmlist_option') == 2) {
				if (!__('dsplus_farmlistFrame')) {
					dsplus_farmlistLink.parentNode.colSpan = Farmlist.colOrder.length;
					$dsplus_farmlistLink.parents('table').eq(0).append(HTML);
				}
			}

			var farmTable = __('dsplus_farmTable');
			farmTable.addEventListener('click', function(e) {
				// Ereignisse etc. binden
				var target = e.target;

				if (target.classList.contains('farm_link')) {
					var coords = target.innerHTML;
					Farmlist.clickCoords(coords, e);
					e.preventDefault();
				} else if (target.classList.contains('spy_only')) {
					var row = target.parentNode.parentNode;
					var farmlink = _C(row, 'farm_link', 0);
					var coords = farmlink.innerHTML;
					Farmlist.clickAge(coords, e);
					e.preventDefault();
				} else if (target.classList.contains('lock-icon')) {
					Farmlist.lockFarm(target, e);
					e.preventDefault();
				} else if (target.classList.contains('del-icon')) {
					Farmlist.delFarm(target, e);
					e.preventDefault();
				} else if (target.classList.contains('sortCol')) {
					Farmlist.sortCol(target, e);
				}
			}, false);
			// append the village-rows
			setTimeout(Farmlist.appendRows, 50);
		},
		createLists: function(hideAttackedFarms) {
			var IDs = Data.villages.list();
			var attackedFarms = Data.settings.get('farmlist_attackedFarms');
			if (typeof(hideAttackedFarms) == 'undefined')
				hideAttackedFarms = (attackedFarms == 3);
			var markAttacks = (attackedFarms == 1 || attackedFarms == 2);
			var moveAttacks = (attackedFarms == 2);
			var min_res = Data.settings.get('farming_minRes');
			var max_age = Data.settings.get('farming_maxAge');
			var max_dist = Data.settings.get('farming_maxDistance');
			// create a real copy of that setting
			max_age = [max_age[0], max_age[1] * 3600];

			Farmlist.normalList = [];
			Farmlist.attackList = [];
			for (var i = 0, ID; ID = IDs[i]; i++) {
				var age = Data.villages.getLastTimeDiff(ID);
				if (!Data.villages.existSpy(ID) || max_age[0] && age > max_age[1])
					continue;

				var attack = Data.commands.getTo(ID).length;
				if (attack && hideAttackedFarms)
					continue;

				var coords = Data.villages.getCoords(ID).split('|');
				var coordDiff = lib.getCoordsDiff(0, coords);
				if (max_dist[0] && coordDiff > max_dist[1])
					continue;

				var coordDiff2 = Math.round(coordDiff * 100) / 100,
				res = Data.villages.getRes(ID);
				if (min_res[0] && res.sum < min_res[1])
					continue;

				var resProd = Data.villages.getResProd(ID);
				var resWarn = Data.villages.getResWarn(ID, null, null, 1, true, res);
				var resWarn90 = Data.villages.getResWarn(ID, null, null, 0.9, true, res);
				var eq = Data.villages.getEQ(ID);
				var wallLevel = Data.villages.getBuilding(ID, 'wall');
				var attrs = '"';
				if (!wallLevel)
					attrs = '" class="grey"';
				else if (wallLevel > 4)
					attrs = ' font-weight: bold;" class="'+ (wallLevel > 9 ? 'warn' : 'warn_90') +'"';

				var ageH = Math.floor(age / 3600);
				var agem = Math.floor((age - ageH * 3600) / 60);
				if (agem < 10) agem = '0' + agem;

				var name = Data.villages.getName(ID);
				var attackImg = attack && markAttacks ? '&nbsp;<img src="'+ image_base +'command/attack.png" alt="">' : '';
				var HTML = '<td class="'+ (Data.villages.isBarbarian(ID) ? '' : 'first-player') +'" style="'+ Farmlist.cols.attack.cellStyle +'"></td>' +
				'<td style="'+ Farmlist.cols.coords.cellStyle +'"><a class="farm_link tooltip" title="'+ name +'" href="javascript:;">'+ coords.join('|') +'</a>'+ attackImg +'</td>' +
				'<td class="wood'+ (resWarn90.wood ? (resWarn.wood ? ' warn' : ' warn_90') : '') +'" title="'+ resProd.wood +'" style="text-align: right;'+ Farmlist.cols.wood.cellStyle +'">'+ lib.formatNumber(res.wood, true, true) +'</td>' +
				'<td class="stone'+ (resWarn90.stone ? (resWarn.stone ? ' warn' : ' warn_90') : '') +'" title="'+ resProd.stone +'" style="text-align: right;'+ Farmlist.cols.stone.cellStyle +'">'+ lib.formatNumber(res.stone, true, true) +'</td>' +
				'<td class="iron'+ (resWarn90.iron ? (resWarn.iron ? ' warn' : ' warn_90') : '') +'" title="'+ resProd.iron +'" style="text-align: right;'+ Farmlist.cols.iron.cellStyle +'">'+ lib.formatNumber(res.iron, true, true) +'</td>' +
				'<td style="display: none;">'+ res.max +'</td>' +
				'<td class="sum'+ (resWarn90.sum ? (resWarn.sum ? ' warn' : ' warn_90') : '') +'" title="'+ resProd.sum +'" style="text-align: right;'+ Farmlist.cols.sum.cellStyle +'">'+ lib.formatNumber(res.sum, true, true) +'</td>' +
				'<td style="text-align: right;'+ Farmlist.cols.eq.cellStyle +'">'+ Math.round(eq * 100) +' <i>%</i></td>';

				if (Data.villages.noUnits(ID))
					HTML += '<td style="text-align: right;'+ Farmlist.cols.unitsInfo.cellStyle +'"><img src="'+ image_base +'quests/completed.png" alt="" class="tooltip" title="'+ texts.gui.noUnits +'" style="width: 19px; height: 16px;"></td>';
				else
					HTML += '<td style="text-align: right;'+ Farmlist.cols.unitsInfo.cellStyle +'"><img src="'+ image_base +'delete.png" alt="'+ texts.gui.x +'" class="tooltip" title="'+ texts.gui.yesUnits +'"></td>';

				HTML += '<td style="text-align: right;'+ Farmlist.cols.distance.cellStyle +'">'+ coordDiff2 +'</td>' +
				'<td style="text-align: right;'+ Farmlist.cols.wall.cellStyle + attrs +'>'+ wallLevel +'</td>';

				HTML += '<td style="text-align: right;'+ Farmlist.cols.age.cellStyle +'">';
				var title = texts.gui.sendOnlySpys;
				var style = '';
				var lastReportID = Data.villages.getLastReportID(ID);
				if (Data.reports.getType(lastReportID) != 'spy') {
					title += '<br>'+ texts.gui.lastAttackNoEspionage;
					style = ' style="color: orange;"';
				}
				HTML += '<a class="spy_only tooltip" title="'+ title +'"'+ style +' href="javascript:;">' +
						ageH +':'+ agem +
					'</a>' +
				'</td>';
				HTML += '<td style="'+ Farmlist.cols.lock.cellStyle +'"><a href="javascript:;" class="cancel-icon lock-icon solo tooltip" title="'+ texts.gui.lockFarm +'"></a></td>';
				HTML += '<td style="'+ Farmlist.cols['delete'].cellStyle +'"><a href="javascript:;" class="cancel-icon del-icon solo tooltip" title="'+ texts.gui['delete'] +'"></a></td>';

				var tr = doc.createElement('tr');
				tr.className = (attack && markAttacks ? 'attack' : '');
				tr.innerHTML = HTML;

				var arr = [0, tr, ID];
				arr[0] = {
					wood: res.wood,
					stone: res.stone,
					iron: res.iron,
					sum: res.sum,
					eq: eq,
					distance: coordDiff,
					wall: wallLevel,
					age: age
				}[Farmlist.sortKey];
				arr[0] = arr[0] * Farmlist.sortDir + parseInt(coords.join(''), 10) / 1000000000;

				if (attack && moveAttacks) {
					Farmlist.attackList.push(arr);
				} else {
					Farmlist.normalList.push(arr);
				}
			}

			Farmlist.normalList.sort(function(a,b) {
				return a[0] - b[0];
			});
			Farmlist.attackList.sort(function(a,b) {
				return a[0] - b[0];
			});
		},
		appendRows: function() {
			Farmlist.createLists();

			var fragment = document.createDocumentFragment();
			for (var i = 0, farm; farm = Farmlist.normalList[i]; i++)
				fragment.appendChild(farm[1]);
			for (var i = 0, farm; farm = Farmlist.attackList[i]; i++)
				fragment.appendChild(Farmlist.attackList[i][1]);

			var tooltipsCreated = false;
			var farmTable = __('dsplus_farmTable');
			var createTooltips = function(e) {
				var target = e.target;
				var $target = jQuery(target);
				if (!tooltipsCreated && target.classList.contains('tooltip')) {
					UI.ToolTip(_C(doc, 'tooltip'), {});
					tooltipsCreated = true;
					farmTable.removeEventListener('mouseover', createTooltips, false);
					$target.triggerHandler('mouseover');
				}
			};
			farmTable.addEventListener('mouseover', createTooltips, false);
			farmTable.appendChild(fragment);

			// display the number of villages
			__('dsplus_coords_count').innerHTML = Farmlist.normalList.length + Farmlist.attackList.length;

			if (Data.settings.get('farmlist_option') != 1) {
				var rows = farmTable.rows;
				for (var i = 1, max = Math.min(rows.length, 11); i < max; i++) {
					var row = rows[i];
					var numLink = _ce('a', i, {
						'href': 'javascript:;',
						'onclick': 'this.parentNode.nextElementSibling.children[0].click();',
						'class': 'float_left',
						'style': 'position: relative;'
					});
					_prepend(numLink, row.cells[0]);
					numLink.style.left = -(numLink.offsetWidth + 6) +'px';
					numLink.style.width = '0px';
				}
			}

			Storage.colorize = Data.settings.get('storageColor_enabledAt', 'place');
			window.setTimeout(function() { Storage.colorRes(); }, 50);

			window.setTimeout(function() { Farmlist.changeCoords(); }, 100);
		},
		click: function(i) {
			if (!Data.settings.get('farmlist_option') || screen != 'place' || !__('units_form'))
				return;

			var farmTable = __('dsplus_farmTable');
			if (!farmTable)
				return;

			var rows = farmTable.rows;
			if (!rows)
				return;

			var link = _T(rows[i], 'a', 0);
			// maybe is the farmlist folded...
			if (link)
				link.click();
		}
	};


	var PA_Features = {
		fa_button: Data.get('am_farm_button', game_data.village.id),
		fa_hide_attacks: Data.get('am_farm_hide_attacks'),
		call_units: lib.storage.getValue('call_units', {}),
		call_res: lib.storage.getValue('call_res', {}),
		quickbar_items: lib.storage.getValue('quickbar_items', [
			{
				name: texts.building.main,
				title: '',
				img: '{graphic}/buildings/main.png',
				link: '{game}&screen=main',
				newTab: false
			},
			{
				name: texts.gui.recruit,
				title: '',
				img: '{graphic}/buildings/barracks.png',
				link: '{game}&screen=train',
				newTab: false
			},
			{
				name: texts.building.snob,
				title: '',
				img: '{graphic}/buildings/snob.png',
				link: '{game}&screen=snob',
				newTab: false
			},
			{
				name: texts.building.smith,
				title: '',
				img: '{graphic}/buildings/smith.png',
				link: '{game}&screen=smith',
				newTab: false
			},
			{
				name: texts.building.place,
				title: '',
				img: '{graphic}/buildings/place.png',
				link: '{game}&screen=place',
				newTab: false
			},
			{
				name: texts.building.market,
				title: '',
				img: '{graphic}/buildings/market.png',
				link: '{game}&screen=market',
				newTab: false
			}
		]),
		insertQuickbar: function() {
			if (lib.hasPA || !Data.settings.get('freePA'))
				return;

			var quickbarTable = _ce('table', '', {
				'id': 'quickbar_outer',
				'style': 'text-align: center; width: 100%; cell-spacing: 0;'
			});
			var quickbar_inner = _ce('table', '', {
				'id': 'quickbar_inner',
				'style': 'border-collapse: collapse;',
				'width': '100%'
			});
			quickbarTable.insertRow(-1).insertCell(-1).appendChild(quickbar_inner);

			var topRow = quickbar_inner.insertRow(-1);
			topRow.className = 'topborder';
			topRow.innerHTML = '<td class="left"> </td><td class="main"> </td><td class="right"> </td>';

			var middleRow = quickbar_inner.insertRow(-1);
			var leftCell = middleRow.insertCell(-1);
			leftCell.className = 'left';
			leftCell.innerHTML = ' ';
			var quickbarMainCell = middleRow.insertCell(-1);
			quickbarMainCell.className = 'main';
			var listTemplate = _ce('ul', '', {
				'class': 'menu nowrap quickbar'
			});
			var list = listTemplate.cloneNode();
			list.classList.remove('nowrap');
			quickbarMainCell.appendChild(list);
			for (var i = 0, max = Data.quickbar.getMax(); i < max; i++) {
				if (Data.quickbar.isLinebreak(i)) {
					list = listTemplate.cloneNode();
					quickbarMainCell.appendChild(list);
				} else {
					var li = _ce('li');
					var targetFrame = Data.quickbar.isNewTab(i) ? ' target="_blank"' : '';
					var title = Data.quickbar.getTitle(i);
					var href = Data.quickbar.getTarget(i).replace('{game}', game_data.link_base_pure.replace('&screen=', ''));
					var imgSrc = Data.quickbar.getImg(i).replace('{graphic}', image_base);
					var name = Data.quickbar.getName(i);
					li.innerHTML = '<span>' +
						'<a'+ targetFrame +' title="'+ title +'"" href="'+ href +'"><img src="'+ imgSrc +'" alt="">'+ name +'</a>' +
					'</span>';
					list.appendChild(li);
				}
			}
			var rightCell = middleRow.insertCell(-1);
			rightCell.className = 'right';
			rightCell.innerHTML = ' ';

			var bottomRow = quickbar_inner.insertRow(-1);
			bottomRow.className = 'bottomborder';
			bottomRow.innerHTML = '<td class="left"> </td><td class="main"> </td><td class="right"> </td>';

			var shadowRow = quickbar_inner.insertRow(-1);
			shadowRow.innerHTML = '<td class="shadow" colspan="3"><div class="leftshadow"> </div><div class="rightshadow"> </div></td>';

			var beforeEl = _prev(__('header_info'));
			_before(quickbarTable, beforeEl);
		},
		fa_initGUI: function() {
			var am_widget_Farm = __('am_widget_Farm');
			var amFarmTable = _T(am_widget_Farm, 'table', 0);
			var resourceHeader = _T(amFarmTable, 'th', 5);

			if (!__('stone_header') && resourceHeader) {
				resourceHeader.id = 'wood_header';
				resourceHeader.style.textAlign = 'center';
				var resourceImg = _T(resourceHeader, 'img', 0);
				resourceImg.src = image_base +'holz.png';

				var stoneHeader = _ce('th', '', {
					'id': 'stone_header',
					'style': 'text-align: center;'
				});
				var stoneImg = resourceImg.cloneNode(true);
				stoneImg.src = image_base +'lehm.png';
				stoneHeader.appendChild(stoneImg);
				_after(stoneHeader, resourceHeader);

				var ironHeader = _ce('th', '', {
					'id': 'iron_header',
					'style': 'text-align: center;'
				});
				var ironImg = resourceImg.cloneNode(true);
				ironImg.src = image_base +'eisen.png';
				ironHeader.appendChild(ironImg);
				_after(ironHeader, stoneHeader);

				var hideLowResources = Data.settings.get('fa_filter_minRes', 0);
				var minResources = Data.settings.get('fa_filter_minRes', 1);
				var hideBigDistance = Data.settings.get('fa_filter_maxDistance', 0);
				var maxDistance = Data.settings.get('fa_filter_maxDistance', 1);
				var rows = amFarmTable.rows;
				for (var i = 0, len = rows.length; i < len; i++) {
					var row = rows[i];
					var numTds = _T(row, 'td').length;
					if (numTds > 1) {
						var resTypes = ['wood', 'stone', 'iron'];
						var resTypeIdx = 0;
						var res = {};
						var resClass = {};
						var resAmount = {};
						var resourceCell = row.cells[5];
						var spans = _T(row.cells[5], 'span');
						for (var j = 0, span; (span = spans[j]) && resTypeIdx < 3; j++) {
							if (span.classList.contains('res') || span.classList.contains('warn') || span.classList.contains('warn_90')) {
								var resType = resTypes[resTypeIdx++];
								res[resType] = span.innerHTML;
								resClass[resType] = span.className;
								var value = parseInt(span.textContent.replace(/\./g, ''), 10);
								resAmount[resType] = value;
							}
						}

						resourceCell.innerHTML = res.wood || '?';
						resourceCell.className = resClass.wood || 'res';
						resourceCell.style.textAlign = 'right';

						var stoneCell = _ce('td', (res.stone || '?'), {
							'class': resClass.stone || 'res',
							'style': 'text-align: right;'
						});
						_after(stoneCell, resourceCell);

						var ironCell = _ce('td', (res.iron || '?'), {
							'class': resClass.iron || 'res',
							'style': 'text-align: right;'
						});
						_after(ironCell, stoneCell);

						if (hideLowResources) {
							var resSum = resAmount.wood + resAmount.stone + resAmount.iron;
							if (resSum < minResources)
								_hide(row);
						}
						if (hideBigDistance) {
							var distance = parseFloat(row.cells[9].textContent);
							if (distance > maxDistance)
								_hide(row);
						}
					} else if (numTds > 0) {
						row.cells[0].colSpan += 2;
					}
				}
			}

			if (!__('dsplus_hide_attacks')) {
				var newRow = amFarmTable.rows[0].cloneNode(true);
				newRow.id = 'dsplus_row';

				var cell = newRow.cells[0];
				_remove(cell.children);

				var hideAttacksCheckbox = _ce('input', '', {
					'id': 'dsplus_hide_attacks',
					'type': 'checkbox'
				});
				hideAttacksCheckbox.checked = PA_Features.fa_hide_attacks;
				hideAttacksCheckbox.addEventListener('change', PA_Features.fa_hideAttacks);
				cell.appendChild(hideAttacksCheckbox);
				cell.appendChild(_text(' '));

				var hideAttacksLabel = _ce('label', texts.gui.farmAssistent.hide_attacks, {
					'for': 'dsplus_hide_attacks'
				});
				cell.appendChild(hideAttacksLabel);

				var clickerDiv = _ce('div', '', {
					'class': 'float_right'
				});
				var farmBtnSelect = _ce('select');
				var btnTypes = ['a', 'b', 'c'];
				for (var i = 0, btnType; btnType = btnTypes[i]; i++) {
					if (_C(amFarmTable, 'farm_icon_'+ btnType).length > 0) {
						var option = _ce('option', texts.gui.farmAssistent['option'+ btnType.toUpperCase()], {
							'value': btnType
						});
						option.selected = (PA_Features.fa_button == btnType);
						farmBtnSelect.appendChild(option);
					}
				}
				farmBtnSelect.addEventListener('change', function() {
					PA_Features.fa_button = farmBtnSelect.value;
					Data.set('am_farm_button', game_data.village.id, PA_Features.fa_button);
					Data.save();
				});
				clickerDiv.appendChild(farmBtnSelect);
				var clickBtn = _ce('input', '', {
					'id': 'dsplus_am_farm_click',
					'type': 'button',
					'class': 'btn',
					'value': texts.gui.farmAssistent.click
				});
				clickBtn.addEventListener('click', function() {
					PA_Features.fa_button = farmBtnSelect.value;
					Data.set('am_farm_button', game_data.village.id, PA_Features.fa_button);
					Data.save();
					PA_Features.fa_clickButton();
				});
				clickerDiv.appendChild(clickBtn);
				cell.appendChild(clickerDiv);
				_after(newRow, amFarmTable.rows[0]);


				PA_Features.fa_hideAttacks();
				Style.am_farm();
				PA_Features.fa_colorAttackedRows();
			}
		},
		fa_colorAttackedRows: function() {
			var widget = __('am_widget_Farm');
			var rows = _T(widget, 'tr');
			for (var i = 0, row; row = rows[i]; i++) {
				var classList = row.classList;
				if (classList.contains('row_a') || classList.contains('row_b')) {
					if (regex.attackImgPath.test(row.cells[3].innerHTML)) {
						classList.add('attack');
					}
				}
			}
		},
		fa_hideAttacks: function() {
			var hideAttacks = __('dsplus_hide_attacks').checked;
			PA_Features.fa_hide_attacks = hideAttacks;
			Data.set('am_farm_hide_attacks', PA_Features.fa_hide_attacks);
			Data.save();

			var widget = __('am_widget_Farm');
			var rows = _T(widget, 'tr');
			for (var i = 0, row; row = rows[i]; i++) {
				var classList = row.classList;
				if (classList.contains('row_a') || classList.contains('row_b')) {
					if (hideAttacks && row.cells.length > 3 && regex.attackImgPath.test(row.cells[3].innerHTML)) {
						_hide(row);
					} else if (!PA_Features.fa_hide_attacks && !classList.contains('inactive') && regex.attackImgPath.test(row.cells[3].innerHTML)) {
						_show(row);
					}
					if (regex.attackImgPath.test(row.cells[3].innerHTML)) {
						classList.add('attack');
					}
				}
			}
		},
		fa_clickButton: function() {
			if (jQuery('.autoHideBox.error').length)
				return;
			var clicked = false;
			var farmIcons = _C(__('am_widget_Farm'), 'farm_icon_'+ PA_Features.fa_button);
			for (var i = 0, len = farmIcons.length; i < len && !clicked; i++) {
				var farmIcon = farmIcons[i];
				var row = farmIcon.parentNode.parentNode;
				if (row.style.display == 'none' || farmIcon.classList.contains('farm_icon_disabled'))
					continue;

				var attackImg = _T(row.cells[3], 'img', 0);
				var runningAttack = attackImg && regex.attackImgPath.test(attackImg.src);
				if (runningAttack && Data.settings.get('fa_skipAttackedVillages'))
					continue;

				farmIcon.click();
				farmIcon.classList.add('farm_icon_disabled');
				clicked = true;
			}
			if (clicked) {
				var max = 700;
				if (!Math.floor(Math.random()*50))
					max += Math.floor(Math.random()*1000);
				setRandom(PA_Features.fa_clickButton, 300, max);
			}
		},
		call_clickInsert: function() {
			var rows = __('village_troup_list').rows;
			for (var i = 1, len = rows.length; i < len; i++)
			{
				var inputs = _T(rows[i], 'input');
				if (inputs.length > 1)
				{
					for (var j = 0, lenj = inputs.length; j < lenj; j++)
					{
						var unit = inputs[j].name;
						if (unit)
						{
							var value = __('dsplus_'+ unit).value;
							inputs[j].value = value;
						}
					}
				}
			}
		},
		call_res_clickInsert: function() {
			var rows = __('village_list').rows;
			for (var i = 1, len = rows.length; i < len; i++)
			{
				var inputs = _T(rows[i], 'input');
				if (inputs.length > 1)
				{
					for (var j = 0, lenj = inputs.length; j < lenj; j++)
					{
						var resType = inputs[j].name;
						if (resType)
						{
							var value = __('dsplus_call_'+ resType).value;
							inputs[j].value = value;
						}
					}
				}
			}
		},

		addInfomarks: function() {
			if (location.href.indexOf('&feature=') != -1 && location.href.indexOf('&feature=Premium') == -1)
				return;

			Style.add().set(
				'#advantages li > img { height: 15px; position: absolute; right: -30px; }'
			);
			// DEV-MARKER >>>
			var localTexts = {
				'full': 'DS+ Free-PA supports and simulates this feature <u>completely</u>.',
				'additions': 'DS+ makes some <u>additions</u> to this feature.',
				'partially': 'DS+ Free-PA supports and simulates <u>parts</u> of this feature.'
			};
			// DEV-MARKER <<<

			var supportedFeatures = [0, 'full', 0, 0, 'full', 'additions', 'partially', 'partially', 0, 0, 0, 0, 0, 0, 0];
			var questionmark = _ce('img', '', {
				'class': 'tooltip',
				'src': '/graphic/questionmark.png'
			});
			var listItems = _T(__('advantages'), 'li');
			for (var i = 0, max = listItems.length; i < max; i++) {
				if (supportedFeatures[i]) {
					var infoElement = questionmark.cloneNode();
					infoElement.title = localTexts[supportedFeatures[i]];
					listItems[i].appendChild(infoElement);
				}
			}
		}
	};


	var Map = {
		// 15x15
		stopImg: '',
		attentionImg: '',
		infos: Data.settings.get('map_integrateInfos'),
		vills: win && win.TWMap && win.TWMap.villages,
		ids_coords: {},
		ownOvl: Data.get('map_ownOvl'),
		otherOvl: Data.get('map_otherOvl'),
		mapSize: Data.get('mapSize'),
		map_sizes: [4,5,7,8,9,11,13,15,20,30],
		minimapSize: Data.get('minimapSize'),
		minimap_sizes: [20,30,40,50,60,70,80,90,100,110,120],
		popup_options: Data.get('map_popup_options'),
		// wird durch onChange aufgerufen
		changeSelectedOverlay: function() {
			var type = this.id.replace('dsplus_', '').replace('Ovl', '');
			Map[type +'Ovl'] = this.value;
			Data.set('map_'+ type +'Ovl', this.value);
			Data.save();

			var els = doc.querySelectorAll('div.dsplus_Info_overlay');
			for (var i = 0, el; el = els[i]; i++) {
				var id = el.id.replace('dsplus_Info_', '');
				var img = __('map_village_'+ id);
				var coord = Map.ids_coords[id];
				Map.updateOverlay(id, coord, img);
			}
		},
		updateMap: function(e) {
			var img = e.target;
			if (img.nodeName != 'IMG' || !img.id || img.id.indexOf('map_village_') != 0 || img.className == 'dsplus_farmStop')
				return;

			var id = img.id.replace('map_village_', '');
			if (id == 'undefined')
				return;
			if (!Map.ids_coords.hasOwnProperty(id)) {
				for (var coord in Map.vills) {
					if (!Map.ids_coords.hasOwnProperty(Map.vills[coord].id))
						Map.ids_coords[Map.vills[coord].id] = coord;
				}
			}

			if (Data.settings.get('map_overlays_enabled'))
				Map.updateOverlay(id, Map.ids_coords[id], img);
		},
		updateOverlay: function(id, coord, el) {
			var div = document.getElementById('dsplus_Info_'+ id);
			var dat = Map.vills[coord];
			if (!dat)
				return;
			var own = dat.owner == game_data.player.id;
			if (!div) {
				div = document.createElement('div');
				div.id = 'dsplus_Info_'+ id;
				div.className = 'dsplus_Info_overlay';
				div.style.left = el.style.left;
				div.style.top = el.style.top;
				el.parentNode.appendChild(div);
			}
			if (own && Map.ownOvl == 'none' || !own && Map.otherOvl == 'none') {
				div.innerHTML = '';
			} else if (own && Map.ownOvl == 'points' || !own && Map.otherOvl == 'points') {
				div.innerHTML = '<div class="ovl_points">'+ dat.points +'</div>';
			} else if (own && Map.ownOvl == 'name' || !own && Map.otherOvl == 'name') {
				div.innerHTML = '<div class="ovl_name">'+ escapeHTML(dat.name, '&shy;') +'</div>';
			} else if (own && Map.ownOvl == 'coords' || !own && Map.otherOvl == 'coords') {
				var xy = parseInt(coord, 10);
				var x = Math.floor(xy / 1000);
				var y = xy - x*1000;
				div.innerHTML = '<div class="ovl_coords">'+ x +'|'+ y +'</div>';
			} else if (!own && Map.otherOvl == 'eq') {
				if (Data.villages.exist(id)) {
					div.innerHTML = '<div class="ovl_eq">'+ Math.round(Data.villages.getEQ(id) * 100) +'</div>';
				} else {
					div.innerHTML = '';
				}
			} else {
				div.innerHTML = '';
			}

			if (Data.villages.isLocked(id)) {
				var img = __('dsplus_farmStop_'+ id);
				var show = Data.get('map_showStop');
				if (!img && show) {
					var obj = document.createElement('img');
					obj.id = 'dsplus_farmStop_'+ id;
					obj.className = 'dsplus_farmStop';
					obj.src = Map.stopImg;
					obj.setAttribute('style', 'position: absolute; z-index: 5; margin-left: 18px; margin-top: 9px; left: '+ el.style.left +'; top: '+ el.style.top +';');
					el.parentNode.appendChild(obj);
				} else if (img && !show) {
					_remove(img);
				}
			}

			if (!lib.hasPA && Data.settings.get('freePA')) {
				if (Data.commands.getTo(id).length) {
					var obj = document.createElement('img');
					obj.id = 'map_cmdicons_'+ id +'_0';
					obj.src = image_base +'map/attack.png';
					obj.setAttribute('style', 'position: absolute; z-index: 4; margin-top: 0px; margin-left: '+ (Data.commands.getReturns(id).length ? 12 : 32) +'px; left: '+ el.style.left +'; top: '+ el.style.top +';');
					el.parentNode.insertBefore(obj, el);
				}
				if (Data.commands.getReturns(id).length) {
					var obj = document.createElement('img');
					obj.id = 'map_cmdicons_'+ id +'_1';
					obj.src = image_base +'map/return.png';
					obj.setAttribute('style', 'position: absolute; z-index: 4; margin-top: 0px; margin-left: 32px; left: '+ el.style.left +'; top: '+ el.style.top +';');
					el.parentNode.insertBefore(obj, el);
				}
			}
		},
		displayForVillageAddOn: function() {
			/* DS+ Code-Inject begin */
			var id = village.id;
			dat.dsplus = {
				mood: 100,
				template: {
					building: '',
					unit: ''
				},
				resClass: null,
				res: null,
				buildings: null,
				units: null
			};
			var colorizableContent = false;
			var storageColorActive = Data.settings.get('storageColor_enabledAt', 'map');

			if (Map.popup_options.buildTemplate) {
				var buildTemplateIdx = Data.templates.getSelected('build', id);
				var buildTemplate = Data.templates.get('build_templates', buildTemplateIdx);
				if (buildTemplate)
					dat.dsplus.template.building = buildTemplate.name;
			}
			if (Map.popup_options.unitTemplate) {
				var unitTemplateIdx = Data.templates.getSelected('unit', id);
				var unitTemplate = Data.templates.get('unit_templates', unitTemplateIdx);
				if (unitTemplate)
					dat.dsplus.template.unit = unitTemplate.name;
			}
			if (Map.popup_options.farmInfos && Data.villages.existSpy(id)) {
				var buildingConstruction = {};
				var specialEvent = dat.points === '';
				if (!specialEvent) {
					var momPoints = dat.points === '' ? -1 : parseInt(dat.points.replace(/\./g, ''), 10);
					var pointsDiff = momPoints - Data.villages.getPoints(id);
					if (pointsDiff > 0)
						/* TODO: move! */
						buildingConstruction = Village.getBuildUps(id, pointsDiff);

					dat.points += ' <span style="font-size: 90%;';
					if (pointsDiff > 0)
						dat.points += ' color: green !important;">+<span id="getBuildUps2" title="'+ texts.gui.buildUps2 +'" style="cursor: pointer;">';
					else if (pointsDiff < 0)
						dat.points += ' color: red;">';
					else
						dat.points += '">'+ lib.plusMinus;
					dat.points += pointsDiff;
					if (pointsDiff > 0)
						dat.points += '</span>';
					dat.points += '</span>';
				}


				dat.dsplus.mood = Data.villages.getMood(id);

				dat.dsplus.spyDate = Data.villages.getSpyDate(id, texts.locale.dateFormat);

				colorizableContent = true;
				var res = Data.villages.getRes(id, null, false);
				var resWarn = Data.villages.getResWarn(id, null, false);
				var resWarn90 = Data.villages.getResWarn(id, null, false, 0.9);
				/* Rohstoffe */
				dat.dsplus.resClass = {
					wood: resWarn.wood && !storageColorActive ? 'warn' : (resWarn90.wood && !storageColorActive ? 'warn_90' : 'res'),
					stone: resWarn.stone && !storageColorActive ? 'warn' : (resWarn90.stone && !storageColorActive ? 'warn_90' : 'res'),
					iron: resWarn.iron && !storageColorActive ? 'warn' : (resWarn90.iron && !storageColorActive ? 'warn_90' : 'res')
				};
				dat.dsplus.res = {
					wood: lib.formatNumber(res.wood, true, true),
					stone: lib.formatNumber(res.stone, true, true),
					iron: lib.formatNumber(res.iron, true, true),
					max: lib.formatNumber(res.max, true, true)
				};

				/* buildings table */
				if (Data.villages.hasBuildings(id)) {
					var line1, line2, line3;
					line1 = line2 = line3 = '';
					for (var key in lib.buildingInfo) {
						var buildingLevel = Data.villages.getBuilding(id, key);
						var newLevel = buildingConstruction.hasOwnProperty(key) ? buildingConstruction[key] : 0;
						if (buildingLevel || newLevel) {
							line1 += '<td style="text-align: center; width: 25px;'+ (line1 ? ' border-left: 1px dashed #C1A264;' : '') +'"><img src="'+ lib.buildingImgPath.replace('%building%', key) +'" alt=""></td>';
							line2 += '<td style="text-align: center;'+ (line2 ? ' border-left: 1px dashed #C1A264;' : '') +'">';
							if (key == 'wall' && buildingLevel > 4)
								line2 += '<span class="'+ (buildingLevel > 9 ? 'warn' : 'warn_90') +'">'+ buildingLevel +'</span>';
							else
								line2 += buildingLevel;
							line2 += '</td>';
							line3 += '<td style="text-align: center;'+ (line3 ? ' border-left: 1px dashed #C1A264;' : '') +'">'+ (newLevel ? '<span style="color: grey; font-size: 80%;">('+ newLevel +')</span>' : '') +'</td>';
						}
					}
					if (line1 || line2 || line3)
						dat.dsplus.buildings = [line1, line2, line3];
				}
			}
			if (Map.popup_options.farmInfos && Data.villages.exist(id)) {
				/* units table */
				var line1, line2;
				line1 = line2 = '';
				for (var key in lib.unitInfo) {
					line1 += '<td style="text-align: center; width: 25px;'+ (line1 ? 'border-left: 1px dashed #C1A264;' : '') +'"><img src="'+ lib.unitImgPath.replace('%unit%', key) +'" alt=""></td>';
					line2 += '<td style="text-align: center;'+ (line2 ? ' border-left: 1px dashed #C1A264;' : '') +'" class="unit-item';
					var unit = Data.villages.getUnit(id, key);
					if (unit < 1) {
						line2 += ' hidden';
						if (unit < 0) unit = '?';
					}
					line2 += '">'+ unit +'</td>';
				}
				if (line1 || line2) dat.dsplus.units = [line1, line2];
			}
			if (!lib.hasPA && Data.settings.get('freePA')) {
				if (Map.popup_options.units_times) {
					var coordDiff = lib.getCoordsDiff(0, [x, y], 0);
					if (coordDiff) {
						dat.units_display.time = true;
						for (var unit in lib.unitInfo) {
							var uSpeed = lib.unitInfo[unit].speed;
							if (uSpeed > 1) {
								dat.units.push({
									image: 'unit/unit_'+ unit +'.png',
									time: Infos.formatRuntime(coordDiff * uSpeed)
								});
							}
						}
					}
				}
			}
			/* DS+ Code-Inject end */

			jQuery('#map_popup').html(win.jstpl('tpl_popup', dat));

			/* DS+ Code-Inject begin */
			if (colorizableContent) {
				Storage.colorize = storageColorActive;
				Storage.colorRes();
			}
			/* DS+ Code-Inject end */
		}
	};




	// --------------------
	// -    Funktionen    -
	// --------------------

	function escapeHTML(str, sep)
	{
		var ret = '';
		if (str)
		{
			sep = sep || sep;
			for (var i = 0, c; c = str.charAt(i); i++)
			{
				switch (c)
				{
				case '&':
					ret += '&amp;';
					break;
				case '<':
					ret += '&lt;';
					break;
				case '>':
					ret += '&gt;';
					break;
				default:
					ret += c+sep;
				}
			}
		}
		return ret;
	}

	function is_array(obj)
	{
		return typeof(obj) == 'object' && (obj instanceof Array);
	}

	function setRandom(fn, min, max)
	{
		window.setTimeout(fn, Math.floor(min + Math.random() * (max - min)));
	}

	function recreateToolTip()
	{
		// das Attribut '.tooltipText' ist nur so erreichbar:
		var script = _ce('script','$(".tooltip").each(function() { if (this.tooltipText) this.title = this.tooltipText; });', { type: 'text/javascript' });
		doc.body.appendChild(script);
		UI.ToolTip(_C(doc, 'tooltip'), {});
	}

	function getCursorPosition(el)
	{
		var pos = 0;
		if ('selectionStart' in el)
		{
			pos = el.selectionStart;
		}
		else if ('selection' in document)
		{
			el.focus();
			var Sel = document.selection.createRange();
			var SelLength = document.selection.createRange().text.length;
			Sel.moveStart('character', -el.value.length);
			pos = Sel.text.length - SelLength;
		}
		return pos;
	}
	function setCursorPosition(el, pos)
	{
		if (el.setSelectionRange)
		{
			el.setSelectionRange(pos, pos);
		}
		else if (el.createTextRange)
		{
			var range = el.createTextRange();
			range.collapse(true);
			range.moveEnd('character', pos);
			range.moveStart('character', pos);
			range.select();
		}
	}

	/**********************\
	 * specific functions *
	\**********************/

	function testWhiteScreenBug()
	{
		if (location.href.indexOf('/game.php') != -1 && (!document.body.innerHTML || !__('serverTime')))
		{
			location.reload();
			throw new Error('Whitescreen bug appeared on "'+ location.href +'"');
		}
	}

	function findLinkContains(str)
	{
		var links = _T(__('content_value'), 'a');
		var searchedLink;
		for (var i = 0, max = links.length; i < max && !searchedLink; i++)
		{
			var link = links[i];
			if (link.text.indexOf(str) > -1)
			{
				searchedLink = link;
			}
		}
		return searchedLink;
	}

	function getUsableUnits()
	{
		var usableUnits = {
			normalCount: 0
		};
		var unitPreferences = Data.settings.get('farming_unitPreferences');
		var calcUnitNumbers = Data.settings.get('farming_calcUnitNumbers');
		var useMinPerUnit = (calcUnitNumbers == 1);
		var useFixPerUnit = (calcUnitNumbers == 2);
		var useMinimum = (calcUnitNumbers == 3);
		var useFixed = (calcUnitNumbers == 4);
		var reachedMinNum = true;
		for (var unit in lib.unitInfo)
		{
			var unitInput = __('unit_input_'+ unit);
			if (!unitInput)
				continue;

			var numLink = _T(unitInput.parentNode, 'a', 'last');
			var match = numLink.textContent.match(/(\d+)/);
			var unitCount = 0;
			if (match)
				unitCount = parseInt(match[1], 10);

			if (unitPreferences[unit])
			{
				var minCount = 0;
				if (useMinPerUnit || useFixPerUnit || useMinimum || useFixed)
					minCount = Math.max(unitPreferences[unit][1] || 0, 0);
				var useThisUnit = unitPreferences[unit][0];

				if (useThisUnit && unitCount < minCount)
				{
					reachedMinNum = false;
				}
				else if (useThisUnit && unitCount > 0)
				{
					usableUnits[unit] = unitCount;
					usableUnits.normalCount += unitCount;
				}
			}
			else if (unitCount > 0)
			{
				var save = false;
				if (unit == 'spy' && Data.settings.get('farming_farmSpys')[0])
				{
					save = true;
				}
				else if (unit == 'ram' && Data.settings.get('farming_sendRams')[0])
				{
					save = true;
				}
				else if (unit == 'catapult' && Data.settings.get('farming_sendCatas'))
				{
					save = true;
				}
				if (save)
				{
					usableUnits[unit] = unitCount;
				}
			}
		}
		if ((useMinimum || useFixed) && !reachedMinNum)
		{
			// remove all farm units
			for (var unit in unitPreferences)
				delete usableUnits[unit];
			usableUnits.normalCount = 0;
		}
		return usableUnits;
	}

	function insertFarmTroops(villageId, usableUnits)
	{

		var maxRuntimeSettings = Data.settings.get('farming_maxRuntime');
		// max Runtime for snobs from top left to bottom right: ~49448 minutes
		var maxRuntime = 9999;
		if (maxRuntimeSettings[0])
			maxRuntime = maxRuntimeSettings[1];

		var unitsToSend = {};
		var vCoords = Data.villages.getCoords(villageId).split('|');
		var coordDiff = lib.getCoordsDiff(0, vCoords);
		var slowestUnit = 0;

		// calculate the amount of rams to send
		var sendRams = Data.settings.get('farming_sendRams');
		var ramCount = usableUnits.ram || 0;
		var wall = Data.villages.getBuilding(villageId, 'wall');
		var wallDestroy = !wall;
		// insert ram
		if (!isTooSlow('ram') && ramCount > 0)
		{
			if (wall >= sendRams[1] && ramCount >= lib.ramsNeeded.reduce[wall])
			{
				wallDestroy = ramCount >= lib.ramsNeeded.destroy[wall];
				if (wallDestroy)
				{
					unitsToSend.ram = lib.ramsNeeded.destroy[wall];
				}
				else
				{
					// not linear! -> i cannot just add the values...
					unitsToSend.ram = lib.ramsNeeded.reduce[wall];
				}
				slowestUnit = Math.max(lib.unitInfo.ram.speed, slowestUnit);
			}
		}

		// calculate the amount of catapults to send
		var cataCount = usableUnits.catapult || 0;
		// insert catapult
		if (!isTooSlow('catapult') && cataCount > 0)
		{
			var buildLevel = 0;
			var building_order = Data.settings.get('farming_buildingPreferences_order');
			var buildingPreferences = Data.settings.get('farming_buildingPreferences');
			for (var i = 0, len = building_order.length; i < len && buildLevel == 0; i++)
			{
				var build = building_order[i];
				var level = Data.villages.getBuilding(villageId, build);
				if (buildingPreferences[build] &&					// building is checked for damaging
					level > lib.buildingInfo[build].min_level &&	// higher than buildings minimum level
					(build != 'wall' || !wallDestroy))				// is not the wall or the wall is not already destroyed through rams
				{
					buildLevel = level;
				}
			}
			if (buildLevel > 0 && cataCount >= lib.catasNeeded.reduce[buildLevel])
			{
				if (cataCount >= lib.catasNeeded.destroy[buildLevel])
				{
					unitsToSend.catapult = lib.catasNeeded.destroy[buildLevel];
				}
				else
				{
					unitsToSend.catapult = lib.catasNeeded.reduce[buildLevel];
				}
				slowestUnit = Math.max(lib.unitInfo.catapult.speed, slowestUnit)
			}
		}


		var farmUnit_order = Data.settings.get('farming_unitPreferences_order');
		var unitPreferences = Data.settings.get('farming_unitPreferences');
		var calcUnitNumbers = Data.settings.get('farming_calcUnitNumbers');
		var useMinPerUnit = (calcUnitNumbers == 1);
		var useFixPerUnit = (calcUnitNumbers == 2);
		var useMinimum = (calcUnitNumbers == 3);
		var useFixed = (calcUnitNumbers == 4);

		// insert minimum amount of units
		var carryRes = 0;
		var reachedMinNum = true;
		if (useMinimum || useFixed)
		{
			for (var unit in unitPreferences)
			{
				var useThisUnit = unitPreferences[unit][0];
				if (!useThisUnit)
					continue;

				var minCount = Math.max(unitPreferences[unit][1] || 0, 0);
				var maxAvailable = usableUnits[unit] || 0;
				if (isFootSoldier(unit) && isTooSlow(unit))
					maxAvailable = 0;

				if (maxAvailable < minCount)
				{
					reachedMinNum = false;
					break;
				}
				if (maxAvailable > 0 && slowestUnit < lib.unitInfo[unit].speed)
					slowestUnit = lib.unitInfo[unit].speed;

				unitsToSend[unit] = minCount;
				carryRes += minCount * lib.unitInfo[unit].carry;
			}
		}
		if (!useFixed && reachedMinNum)
		{
			// set "resSum" to the sum of ressources at this moment
			var resSum = Data.villages.getRes(villageId, lib.getTime() + Math.round(coordDiff * slowestUnit * 60)).sum;
			for (var i = 0, max = farmUnit_order.length; i < max && (slowestUnit == 0 || carryRes < resSum); i++)
			{
				var unit = farmUnit_order[i];
				if (!unitsToSend[unit])
					unitsToSend[unit] = 0;
				var useThisUnit = unitPreferences[unit][0];
				var maxAvailable = usableUnits[unit] || 0;
				maxAvailable -= unitsToSend[unit];
				var minCount = 1;
				if (useFixPerUnit || useMinPerUnit)
					minCount = Math.max(unitPreferences[unit][1] || 0, 0);
				if (maxAvailable < minCount || !useThisUnit || isFootSoldier(unit) && isTooSlow(unit))
					continue;

				if (slowestUnit < lib.unitInfo[unit].speed)
				{
					slowestUnit = lib.unitInfo[unit].speed;
					resSum = Data.villages.getRes(villageId, lib.getTime() + Math.round(coordDiff * slowestUnit * 60)).sum;
				}
				var unitCount = Math.ceil((resSum - carryRes) / lib.unitInfo[unit].carry);
				unitCount = Math.min(unitCount, maxAvailable);
				if (useFixPerUnit)
				{
					unitCount = minCount;
				}
				else if (useMinPerUnit)
				{
					unitCount = Math.max(unitCount, minCount);
				}

				carryRes += unitCount * lib.unitInfo[unit].carry;
				unitsToSend[unit] += unitCount;
			}
		}
		var sendResTroops = carryRes > 0;

		// send rams and catapults only with other units
		if (!sendResTroops)
		{
			unitsToSend.ram = 0;
			unitsToSend.catapult = 0;
		}

		// check number of scouts
		unitsToSend.spy = getFarmSpyCount(sendResTroops, usableUnits.spy, Data.villages.isBarbarian(villageId));

		// there are units to send which are able to carry resources
		for (var unit in lib.unitInfo)
		{
			var unitCount = unitsToSend[unit] || '';
			var unitInput = __('unit_input_'+ unit);
			if (!unitInput)
				continue;

			__('unit_input_'+ unit).value = unitCount;
		}

		// the selected units can carry resources
		return sendResTroops;

		function isTooSlow(unit)
		{
			var unitRuntime = Math.round(coordDiff * lib.unitInfo[unit].speed / 60);
			return (unitRuntime > maxRuntime);
		}
		function isFootSoldier(unit)
		{
			return {
				spear: true
				, sword: true
				, axe: true
				, archer: true
			}[unit];
		}
	}
	function getFarmSpyCount(sendResTroops, maxSpyCount, isBarbarian)
	{
		var farmSpysSettings = Data.settings.get('farming_farmSpys');
		var farmSpys = farmSpysSettings[1];
		if (!sendResTroops && !isBarbarian)
			farmSpys = Math.max(Data.get('spynum_player'), farmSpys);

		// enough scouts in the village
		var enoughSpys = maxSpyCount >= farmSpys;
		// send scouts only with units?
		var sendUnitsUnterCondition = sendResTroops || !sendResTroops && !Data.settings.get('farming_spyOnlyWithUnits');
		// send scouts depending on various factors
		if (enoughSpys && sendUnitsUnterCondition)
		{
			return farmSpys;
		}

		return 0;
	}

	/* Hauptskript */
	function run() {


		if (oldVersion)
			return;


		if (__('script_warning'))
			GUI.hideScriptWarnings();

		if (dynamicLoaded)
			Bot.dynamicLoad();

		/*
		 * Settings.init();
		 */
		if (!dynamicLoaded) {
			if (Data.settings.get('fixQuickbar'))
				GUI.fixQuickbar();
			if (Data.settings.get('fixInfobar'))
				GUI.fixInfobar();

			GUI.settings.insertIcon();
			GUI.addPlayerLink();
		}

		if (Data.settings.get('sitting_enabled') || !UVActive) {
			var content_value = __('content_value');


			/*
			 * Hotkeys.init();
			 */
			if (Data.settings.get('hotkeys_enabled')) {
				if (!dynamicLoaded) {
					// Link-Fix Anfang
					document.body.addEventListener('click', function(e) {
						if (e.target.tagName == 'A')
							e.target.blur();
					});
					// Link-Fix Ende

					document.addEventListener('keydown', Hotkeys.keyDown, false);
					document.addEventListener('keyup', Hotkeys.keyUp, false);
					document.addEventListener('mousemove', CoordSelector.selectCoords, false);
				}

				// Anzeige
				if (Data.settings.get('hotkeys_display')) {
					var key;
					for (var name in Hotkeys.els.all) {
						key = Hotkeys.userKeys.all[name] || Hotkeys.stdKeys.all[name];
						Hotkeys.els.all[name](Hotkeys.char(key), dynamicLoaded);
					}
					for (var name in Hotkeys.els[screen]) {
						key = Hotkeys.userKeys.hasOwnProperty(screen) && Hotkeys.userKeys[screen][name] || Hotkeys.stdKeys[screen][name];
						Hotkeys.els[screen][name](Hotkeys.char(key), dynamicLoaded);
					}
				}

				// Versammlungsplatz:
				if (__('units_form')) {
					__('units_form').addEventListener('submit', function(e) {
						if (!Hotkeys.actions.place.submit(e, true)) {
							e.preventDefault();
							return false;
						}
						return true;
					}, false);
				}
			}

			/*
			 * Storage.init();
			 */
			if (!dynamicLoaded && (
					Data.settings.get('storageColor_enabledAt', 'infobar') ||
					(Data.settings.get('storageColor_enabledAt', 'production') && screen == 'overview_villages') ||
					(Data.settings.get('storageColor_enabledAt', 'market') && screen == 'market') ||
					(Data.settings.get('storageColor_enabledAt', 'place') ) ||
					(Data.settings.get('storageColor_enabledAt', 'map') ) ||
					(Data.settings.get('storageColor_enabledAt', 'info_village') ) ||
					(Data.settings.get('storageColor_enabledAt', 'coin_mint') && screen == 'snob' && game_data.mode == 'coin')
				))
			{
				var colors = Data.settings.get('storageColors');
				var diff = 1;
				var num1 = 0;
				var num2 = 0;
				for (var i = 0; i < 101; i++)
				{
					var tmp = [
						colors[num1][0] + Math.round( (colors[num2][0] - colors[num1][0]) / diff * (i - num1) )
						, colors[num1][1] + Math.round( (colors[num2][1] - colors[num1][1]) / diff * (i - num1) )
						, colors[num1][2] + Math.round( (colors[num2][2] - colors[num1][2]) / diff * (i - num1) )
					];
					Storage.colors[i] = tmp;
					if (colors[i])
					{
						num1 = i;
						for (num2 = i+1; num2 < 101 && !colors[num2]; num2++);
						diff = num2-num1;
					}
				}
			}

			var dt = new Date();
			if (dt.getFullYear() != lib.storage.getValue('april-notfunny', 0) && (dt.getMonth() == 3 && dt.getDate() == 1))
			{
				var firstApril = function()
				{
					var canvas = _ce('canvas');
					var img = _ce('img');
					var link = __('favicon').cloneNode(true);
					var fakes = lib.storage.getValue('fakeIncs', false);
					if (fakes === false)
					{
						fakes = Math.round(Math.random()*5*(+game_data.player.villages));
						lib.storage.setValue('fakeIncs', fakes);
					}
					else if (!Math.floor(Math.random()*25))
					{
						fakes += Math.round(Math.random()*(+game_data.player.villages)/50);
						lib.storage.setValue('fakeIncs', fakes);
					}
					var incs = ((+game_data.player.incomings) + fakes).toString();
					if (!typeof canvas.getContext == 'function' || !typeof canvas.fillText == 'function')
						return;
					var numberString;
					if (incs < 100)
						numberString = incs.toString()
					else if (incs < 1000)
						numberString = '99+'
					else if (incs < 10000)
						numberString = incs.toString().substring(0, 1) + 'k'
					else
						numberString = ': O';
					if (canvas.getContext)
					{
						canvas.height = canvas.width = 16;
						var ctx = canvas.getContext('2d');
						img.onload = function ()
						{
							var fontSize = 8,
							xPos = 2.5,
							yPos = 11;
							if (numberString.length == 1)
							{
								fontSize = 12;
								xPos = 4.5;
								yPos = 12.3
							}
							else if (numberString.length == 2)
							{
								fontSize = 10;
								xPos = 1.5;
								yPos = 11
							}
							ctx.drawImage(this, 0, 0);
							ctx.font = 'bold ' + fontSize + 'px "helvetica", sans-serif';
							ctx.fillStyle = '#333333';
							ctx.fillText(numberString, xPos, yPos);
							link.href = canvas.toDataURL('image/png');
							var current = __('favicon');
							_remove(current);
							document.head.appendChild(link);
						};
						img.src = '/graphic/favicon_lit.png'
					}
					__('header_commands').style.display = 'table-cell';
					__('incomings_cell').style.display = 'table-cell';
					__('incomings_amount').textContent = incs;
				};
				if (/[?&]april=notfunny&?/.test(location.href))
				{
					lib.storage.setValue('april-notfunny', dt.getFullYear());
				}
				else
				{
					firstApril();
				}
			}

			if (!dynamicLoaded && Data.settings.get('storageColor_enabledAt', 'infobar'))
				Storage.colorInfoRes();

			/*
			 * Queues.init();
			 */
			Queues.init();



			/*
			 * Village.init();
			 */
			var table = __('combined_table') || __('production_table');
			if (table && table.rows.length) {
				var villIdx = 0;
				var pointsIdx = -1;
				var ownVills = [];
				var rows = table.rows;
				var headerCells = rows[0].cells;
				for (var i = 0, cell; cell = headerCells[i]; i++) {
					if (texts.regex.village.test(cell.innerHTML))
						villIdx = i;
					else if (texts.regex.points.test(cell.innerHTML))
						pointsIdx = i;
				}
				for (var i = 1, row; row = rows[i]; i++) {
					var match = row.cells[villIdx].innerHTML.match(texts.regex.ownVill);
					if (match) {
						ownVills.push({
							id: parseInt(match[1], 10),
							name: match[2],
							x: parseInt(match[3], 10),
							y: parseInt(match[4], 10),
							con: parseInt(match[5], 10),
							points: pointsIdx != -1 ? parseInt(row.cells[pointsIdx].innerHTML.replace(/<span class="grey">\.<\/span>/g, ''), 10) : 0
						});
					}
				}
				// TODO: move to Data.villages
				Village.own = ownVills;
				lib.storage.setValue('ownVillages', Village.own);
			}
			if (!dynamicLoaded && !lib.hasPA && Data.settings.get('freePA')) {
				if (Village.own.length > 1) {
					var prevLocation = '';
					var nextLocation = '';
					if (Village.own[0].id == Village.THIS) {
						prevLocation = location.href.replace(regex.villageChangeReplacer, '/game.php?$1$2village='+ Village.own[Village.own.length-1].id);
						nextLocation = location.href.replace(regex.villageChangeReplacer, '/game.php?$1$2village='+ Village.own[1].id);
					} else if (Village.own[Village.own.length-1].id == Village.THIS) {
						prevLocation = location.href.replace(regex.villageChangeReplacer, '/game.php?$1$2village='+ Village.own[Village.own.length-2].id);
						nextLocation = location.href.replace(regex.villageChangeReplacer, '/game.php?$1$2village='+ Village.own[0].id);
					} else {
						for (var i = 1, max = Village.own.length-1; i < max; i++) {
							if (Village.own[i].id == Village.THIS) {
								prevLocation = location.href.replace(regex.villageChangeReplacer, '/game.php?$1$2village=' + Village.own[i-1].id);
								nextLocation = location.href.replace(regex.villageChangeReplacer, '/game.php?$1$2village=' + Village.own[i+1].id);
							}
						}
					}
					prevLocation = prevLocation.replace(/&?#.*/, '');
					nextLocation = nextLocation.replace(/&?#.*/, '');

					var beforeEl = __('menu_row2_village');
					// left arrow
					var arrowHTML = '';
					if (!prevLocation) arrowHTML = '<span class="arrowLeftGrey"> </span>';
					else arrowHTML = '<a id="village_switch_left" class="village_switch_link" href="'+ prevLocation +'" accesskey="a"><span class="arrowLeft"> </span></a>';
					var prevCell = _ce('td', arrowHTML, {
						'class': 'box-item icon-box separate arrowCell'
					});
					_before(prevCell, beforeEl);
					// right arrow
					var arrowHTML = '';
					if (!nextLocation) arrowHTML = '<span class="arrowRightGrey"> </span>';
					else arrowHTML = '<a id="village_switch_right" class="village_switch_link" href="'+ nextLocation +'" accesskey="d"><span class="arrowRight"> </span></a>';
					var nextCell = _ce('td', arrowHTML, {
						'class': 'box-item icon-box separate arrowCell'
					});
					_before(nextCell, beforeEl);
				}

				// groups popup:
				var arrowDownCell = _ce(
					'td',
					'<a href="javascript:;" id="open_groups"><span class="icon header arr_down"></span></a>' +
					'<a href="javascript:;" id="close_groups" style="display: none;"><span class="icon header arr_up"></span></a>',
					{
						'class': 'box-item'
					}
				);
				__('menu_row2').appendChild(arrowDownCell);
				var open_groups = __('open_groups');
				var close_groups = __('close_groups');

				Style.groupPopup();
				var group_popup = __('group_popup');
				if (!group_popup) {
					group_popup = _ce(
						'div',
						'<div id="group_popup_menu">' +
							'<a id="group_popup_close" href="javascript:;">'+ texts.gui.X +'</a>' +
						'</div>' +
						'<div id="group_popup_main" style="height: auto; max-height: 950px;">' +
							'<div id="group_popup_content"></div>' +
						'</div>',
						{
							'id': 'group_popup'
						}
					);
					doc.body.appendChild(group_popup);
					UI.Draggable(group_popup);
				}
				var closeLink = _T(__('group_popup_menu'), 'a', 0);
				closeLink.addEventListener('mouseup', function() {
					group_popup.classList.remove('show');
					group_popup.classList.add('hidden');
					_hide(close_groups);
					_show(open_groups);
				}, false);

				open_groups.addEventListener('click', function(e) {
					var HTML = '<form id="select_group_box">' +
						'<input type="hidden" name="mode" value="overview">' +
						'<p style="margin: 0 0 10px 0; font-weight: bold;">'+ texts.gui.freePA.villagePopup.group +':<select id="group_id" name="group_id" style="margin-left: 3px; "><option value="0">'+ texts.gui.freePA.villagePopup.group_all +'</option></select></p>' +
					'</form>' +
					'<div id="group_list_content" style="overflow-x: auto; overflow-y: auto; height: auto; ">' +
						'<table id="group_table" class="vis" width="100%" cellpadding="5" cellspacing="0"><tr><th class="group_label" colspan="2">'+ texts.gui.freePA.villagePopup.village +'</th></tr></table>' +
							'<div id="group_popup_content_container">' +
								'<table id="group_table" class="vis" width="100%" cellpadding="5" cellspacing="0">';
					for (var i = 0; i < Village.own.length; i++) {
						HTML += '<tr>' +
							'<td'+ (Village.own[i].id == Village.THIS ? ' class="selected"' : '') +'><a href="javascript:selectVillage('+ Village.own[i].id +', 0)">'+ Village.own[i].name +'</a></td>' +
							'<td style="font-weight: bold; width: 100px; text-align: right;"'+ (Village.own[i].id == Village.THIS ? ' class="selected"' : '') +'>'+ Village.own[i].x +'|'+ Village.own[i].y +'</td>' +
						'</tr>';
					}
					HTML += '</table>' +
					'</div>';

					e = e || window.event;
					var mx = e.clientX;
					var my = e.clientY;
					var $doc = jQuery(document);
					var pos = {
						x: Math.min(Math.max(mx, 0), $doc.width()),
						y: Math.min(Math.max(my, 60), $doc.height())
					};
					group_popup.style.left = pos.x +'px';
					group_popup.style.top = pos.y +'px';
					group_popup.classList.remove('hidden');
					__('group_popup_content').innerHTML = HTML;
					group_popup.classList.add('show');


					group_popup.style.width = '320px';
					var h3s = _T(group_popup, 'h3');
					for (var i = 0, h3; h3 = h3s[i]; i++) {
						_hide(h3);
					}
					var popup_main = __('group_popup_main');
					popup_main.style.maxHeight = '950px';
					popup_main.style.oveflowY =  'scroll';
					popup_main.style.height = '380px';
					var divs = _T(popup_main, 'div');
					for (var i = 0, div; div = divs[i]; i++) {
						div.style.height = 'auto';
					}

					group_popup.classList.add('show');
					group_popup.classList.remove('hidden');
					_show(close_groups);
					_hide(open_groups);
				}, false);
				close_groups.addEventListener('click', function() {
					group_popup.classList.remove('show');
					group_popup.classList.add('hidden');
					_hide(close_groups);
					_show(open_groups);
				}, false);
			}

			/*
			 * Template.init();
			 */
			 Style.template();

			Template.init();

			/*
			 * PA_Features.init();
			 */
			// Schnellleiste
			if (!dynamicLoaded) {
				PA_Features.insertQuickbar();
				var info_box = _next(__('script_warning'));
				if (info_box && info_box.classList.contains('info_box')) {
					var firstLink = _T(info_box, 'a', 0);
					if (!firstLink && _next(info_box) && _next(info_box).classList.contains('info_box')) {
						info_box = _next(info_box);
						firstLink = _T(info_box, 'a', 0);
					}
					if (firstLink && firstLink.href.indexOf('screen=premium') > -1)
						_hide(info_box);
				}
			}

			if (screen == 'overview_villages') {
				Storage.colorize = Data.settings.get('storageColor_enabledAt', 'production');
				Storage.init_prod();
				Storage.initResTicker();

				/*
				 * Filter.init();
				 */
				if (Data.settings.get('production_filter_enabled')) {
					GUI.filter.initOverview();
				}

				/*
				 * Overview.init();
				 */
				if (__('production_table')) {
					var idx = {};
					var production_table = __('production_table');
					var productionRows = production_table.rows;

					var headerRow = productionRows[0];
					var checkboxRow = _ce('tr', '', {
						'style': 'text-align: center; display: none;'
					});
					_before(checkboxRow, headerRow);

					var cut = Data.settings.get('production_removeCols');
					var headerCells = headerRow.cells;
					for (var i = 0, th; th = headerCells[i]; i++) {
						var newCell = checkboxRow.insertCell(-1);
						if (!idx.hasOwnProperty('build') && texts.regex.build_contract.test(th.innerHTML)) {
							idx.build = i;
							var checkbox = _ce('input', '', {
								'type': 'checkbox',
								'style': 'margin: 0;'
							});
							newCell.appendChild(checkbox);
							_hide(newCell);
							if (cut)
								_hide(th);
						} else if (!idx.hasOwnProperty('research') && texts.regex.research.test(th.innerHTML)) {
							idx.research = i;
							var checkbox = _ce('input', '', {
								'type': 'checkbox',
								'style': 'margin: 0;'
							});
							newCell.appendChild(checkbox);
							_hide(newCell);
							if (cut)
								_hide(th);
						} else if (th.children.length && th.children[0].tagName == 'SPAN') {
							var span = th.children[0];
							if (span.classList.contains('wood')) idx.wood = i;
							else if (span.classList.contains('stone')) idx.stone = i;
							else if (span.classList.contains('iron')) idx.iron = i;
						}
					}
					if (Data.settings.get('storageColor_enabledAt', 'production')) {
						var sum = {
							wood: 0,
							stone: 0,
							iron: 0
						};
						for (var i = 2, row; row = productionRows[i]; i++) {
							var cells = row.cells;
							var woodVal = parseInt(cells[idx.wood].innerHTML.replace(/[^\d]/g, ''), 10);
							sum.wood += woodVal;
							var stoneVal = parseInt(cells[idx.stone].innerHTML.replace(/[^\d]/g, ''), 10);
							sum.stone += stoneVal;
							var ironVal = parseInt(cells[idx.iron].innerHTML.replace(/[^\d]/g, ''), 10);
							sum.iron += ironVal;

							if (cut && idx.hasOwnProperty('build')) _hide(cells[idx.build]);
							if (cut && idx.hasOwnProperty('research')) _hide(cells[idx.research]);
						}
						var sumRow = production_table.insertRow(-1);
						for (var i = 0, max = headerCells.length; i < max; i++) {
							var newCell = _ce('th', '', {
								'class': 'nowrap',
								'style': 'text-align: right'
							});
							if (cut && (idx.build == i || idx.research == i)) _hide(newCell);
							sumRow.appendChild(newCell);
							var key = false;
							if (i == idx.wood) key = 'wood';
							else if (i == idx.stone) key = 'stone';
							else if (i == idx.iron) key = 'iron';
							if (key !== false) {
								newCell.innerHTML = lib.formatNumber(sum[key], true, true, true);
								newCell.title = lib.formatNumber(sum[key], true);
							}
						}
					}
				}
				else if (__('commands_table'))
				{
					/*
					 * Command.init();
					 */

					var sum = {};
					var commands = {};
					var idx = {};
					var commands_table = __('commands_table');
					var firstTH = _T(commands_table, 'th', 0);
					var headerRow = firstTH.parentNode;
					var headerCells = headerRow.cells;
					for (var i = 0, cell; cell = headerCells[i]; i++)
					{
						var img = _T(cell, 'img', 0);
						var match = img && img.src && img.src.match(regex.unitKey);
						if (!match)
							continue;
						idx[i] = match[1];
					}

					var rows = commands_table.rows;
					for (var i = 1, row; row = rows[i]; i++)
					{
						var img = _T(row, 'img', 0);
						var match = img && img.src && img.src.match(regex.commandPath);
						if (!match)
							continue;
						var type = match[1];
						var isFarmAttack = false;
						if (type == 'farm')
						{
							type = 'attack';
							isFarmAttack = true;
						}
						if (texts.regex.commands[type])
						{
							var text = _T(row, 'a', 0).children[0].innerHTML;
							var match = text.match(texts.regex.commands[type]);
							if (!match)
								continue;
							if (!commands[type])
								commands[type] = {};

							var count, units, savedRows, isFarm;
							var ID = match[2] + match[3];
							if (commands[type].hasOwnProperty(ID))
							{
								units = commands[type][ID].units;
								count = commands[type][ID].count;
								savedRows = commands[type][ID].rows;
								isFarm = commands[type][ID].isFarm && isFarmAttack;
							}
							else
							{
								units = {};
								count = 0;
								savedRows = [];
								isFarm = isFarmAttack;
							}
							var cells = row.cells;
							for (var j = 0, cell; cell = cells[j]; j++)
							{
								if (idx[j])
								{
									var k = idx[j];
									var num = parseInt(cell.innerHTML, 10);
									if (units[k])
										units[k] += num;
									else
										units[k] = num;
									if (sum[k])
										sum[k] += num;
									else
										sum[k] = num;
								}
							}
							savedRows.push(row);
							commands[type][ID] = {
								text: text
								, count: ++count
								, units: units
								, rows: savedRows
								, isFarm: isFarm
							};
						}
					}

					var show = Data.get('showCommandSummary');
					var toggleSummaryTable = _ce('a', lib.guillemetRight +' ', {
						'href': 'javascript:;'
						, 'id': 'dsplus_toggleSummaryTable'
					});
					var hideSummaryTable = _ce('span', texts.gui.hideSummaryTable, {
						'style': 'display: '+ (show ? 'inline' : 'none') +';'
					});
					toggleSummaryTable.appendChild(hideSummaryTable);
					var showSummaryTable = _ce('span', texts.gui.showSummaryTable, {
						'style': 'display: '+ (show ? 'none' : 'inline') +';'
					});
					toggleSummaryTable.appendChild(showSummaryTable);

					var summary_table = _ce('table', '', {
						'id': 'summary_table'
						, 'class': 'vis'
						, 'style': 'width: 100%; display: '+ (show ? 'table' : 'none') +';'
					});
					var firstRow = '<th>&#10005;</th><th colspan="3" style="width: 100%;">'+ texts.gui.command +'</th>';
					var lastRow = '<th></th><th colspan="3">' +
						'<a id="toggleAllRows" href="javascript:;">'+ lib.guillemetRight +' '+ texts.gui.expandAllRows +'</a>' +
					'</th>';
					for (var i in idx)
					{
						var unit = idx[i];
						firstRow += '<th style="text-align: center;"><img src="'+ image_base +'unit/unit_'+ unit +'.png" title="'+ texts.unit[unit] +'"></th>';
						lastRow += '<th>'+ (sum[unit] || 0) +'</th>';
					}
					summary_table.insertRow(-1).innerHTML = firstRow;

					for (var type in commands)
					{
						for (var ID in commands[type])
						{
							var imgType = type;
							if (commands[type][ID].isFarm)
								imgType = 'farm';
							var HTML = '<td style="font-weight: bold;">'+ commands[type][ID].count +'</td>' +
								'<td colspan="3"><img src="'+ image_base +'command/'+ imgType +'.png" alt="">&nbsp;<a href="javascript:;" class="toggleLink">'+ commands[type][ID].text +'</a></td>';
							for (var unit in commands[type][ID].units)
							{
								HTML += '<td class="unit-item '+ (!commands[type][ID].units[unit] ? 'hidden' : '') +'">'+ commands[type][ID].units[unit] +'</td>';
							}
							summary_table.insertRow(-1).innerHTML = HTML;

							for (var i = 0, row; row = commands[type][ID].rows[i]; i++)
							{
								var newRow = summary_table.insertRow(-1);
								_hide(newRow);
								newRow.classList.add('details');
								if (i % 2 == 0)
								{
									newRow.classList.add('row_b');
								}
								else
								{
									newRow.classList.add('row_a');
								}
								var img = _T(row, 'img', 0);
								var link = _T(row, 'a', 0);
								newRow.innerHTML = '<td style="background-color: transparent"></td>' +
									'<td>'+ img.outerHTML +' '+ link.outerHTML +'</td>' +
									row.innerHTML.replace('<td>'+ row.cells[0].innerHTML +'</td>', '');
							}
						}
					}

					if (summary_table.rows.length > 1)
					{
						summary_table.insertRow(-1).innerHTML = lastRow;

						toggleSummaryTable.addEventListener('click', function (event)
						{
							show = !show;
							if (show)
							{
								_show(hideSummaryTable);
								_hide(showSummaryTable);
								_show(summary_table);
							}
							else
							{
								_hide(hideSummaryTable);
								_show(showSummaryTable);
								_hide(summary_table);
							}

							Data.set('showCommandSummary', show);
							Data.save();
						});
						_before(toggleSummaryTable, commands_table);

						summary_table.addEventListener('click', function (event)
						{
							var target = event.target;
							if (target.id == 'toggleAllRows')
							{
								var row = summary_table.rows[1];
								while (row && _T(row, 'td').length)
								{
									var showDetails = row.classList.contains('selected');
									if (showDetails)
									{
										row.classList.remove('selected');
									}
									else
									{
										row.classList.add('selected');
									}
									var detailRow = _next(row);
									do
									{
										_toggle(detailRow);
										detailRow = _next(detailRow);
									} while (detailRow.classList.contains('details'));
									row = detailRow;
								}
							}
							else if (target.classList.contains('toggleLink'))
							{
								target.blur();
								var row = target.parentNode.parentNode;
								var showDetails = row.classList.contains('selected');
								if (showDetails)
								{
									row.classList.remove('selected');
								}
								else
								{
									row.classList.add('selected');
								}
								var detailRow = _next(row);
								do
								{
									_toggle(detailRow);
									detailRow = _next(detailRow);
								} while (detailRow.classList.contains('details'));
							}
						});
						_before(summary_table, commands_table);
					}
				}
			}
			else if (screen == 'overview') {
				/*
				 * Overview.init();
				 */
				if (win.hasOwnProperty('VillageOverview') && Data.settings.get('overview_showBooty')) {
					var newVillageOverview = jQuery.extend(
						{
							position: null
						},
						win.VillageOverview,
						{
							init: function() {
								if (win.mobile || !__('overviewtable'))
									return;

								jQuery(jQuery('#overviewtable').get(0).cloneNode(false)).attr('id', 'dsplus_overviewtable').insertAfter('#overviewtable');
								jQuery('#dsplus_overviewtable').append(jQuery('#overviewtable').children());
								jQuery('#overviewtable').remove();
								jQuery('#dsplus_overviewtable').sortable({
									placeholder: 'vis placeholder',
									cursor: 'move',
									items: 'div.moveable',
									handle: 'h4',
									opacity: 0.6,
									start: function() {
										jQuery('div.hidden_widget').fadeTo(0, 0.5);
									},
									stop: function() {
										jQuery('div.hidden_widget').hide();
									},
									update: function() {
										var columns = {
											leftcolumn: [],
											rightcolumn: []
										};
										var widgets = jQuery(this).sortable('toArray');
										for (var i in widgets)
											if (widgets.hasOwnProperty(i)) {
												var parent = document.getElementById(widgets[i]).parentNode.id;
												columns[parent].push(widgets[i]);
											}

										var position;
										for (var key in columns)
											for (var i = 0, max = columns[key].length; i < max; i++) {
												if (columns[key][i] == 'show_booty') {
													if (i) position = [columns[key][i-1], 0, key];
													else if (max != 1) position = [0, columns[key][i+1], key];
													else position = [0, 0, key];
													columns[key].splice(i, 1);
												}
											}
										Data.set('booty_position', position);
										window.setTimeout(function() { Data.save(); });

										jQuery.post(newVillageOverview.urls.reorder, columns);
									}
								});
							}
						}
					);
					newVillageOverview.init();


					var serverTime = lib.getServerTime();
					var today = lib.formatTime2Key(serverTime);
					var bootyToday = {
						wood: Data.booty.get(today, 'wood'),
						stone: Data.booty.get(today, 'stone'),
						iron: Data.booty.get(today, 'iron')
					};
					bootyToday.sum = bootyToday.wood + bootyToday.stone + bootyToday.iron;

					serverTime.setHours(serverTime.getHours() - 24);
					var yesterday = lib.formatTime2Key(serverTime);
					var bootyYesterday = {
						wood: Data.booty.get(yesterday, 'wood'),
						stone: Data.booty.get(yesterday, 'stone'),
						iron: Data.booty.get(yesterday, 'iron')
					};
					bootyYesterday.sum = bootyYesterday.wood + bootyYesterday.stone + bootyYesterday.iron;

					var booty_show = Data.get('booty_show');
					var HTML = '<div id="show_booty" class="vis moveable widget" style="opacity: 1; z-index: 0;">' +
						'<h4><a href="javascript:;">'+ texts.gui.booty +'</a><img src="'+ (booty_show ? 'graphic/minus.png' : 'graphic/plus.png') +'" class="float_right" style="cursor: pointer;"></h4>' +
						'<div style="display:'+ (booty_show ? 'block' : 'none') +';">' +
							'<table width="100%">' +
								'<tr class="nowrap"><td colspan="2"><strong>'+ texts.gui.todayBooty +':</strong></td></tr>' +
								'<tr class="nowrap"><td width="70"><span class="icon header wood"> </span> '+ texts.gui.wood +'</td><td><strong>'+ lib.formatNumber(bootyToday.wood, true, true) +'</strong></td></tr>' +
								'<tr class="nowrap"><td width="70"><span class="icon header stone"> </span> '+ texts.gui.stone +'</td><td><strong>'+ lib.formatNumber(bootyToday.stone, true, true) +'</strong></td></tr>' +
								'<tr class="nowrap"><td width="70"><span class="icon header iron"> </span> '+ texts.gui.iron +'</td><td><strong>'+ lib.formatNumber(bootyToday.iron, true, true) +'</strong></td></tr>' +
								'<tr class="nowrap"><td width="70"><span class="icon header ressources"> </span> '+ texts.gui.total +'</td><td><strong>'+ lib.formatNumber(bootyToday.sum, true, true) +'</strong></td></tr>' +
								'<tr class="nowrap"><td colspan="2"><strong>'+ texts.gui.yesterdayBooty +':</strong></td></tr>' +
								'<tr class="nowrap"><td width="70"><span class="icon header wood"> </span> '+ texts.gui.wood +'</td><td><strong>'+ lib.formatNumber(bootyYesterday.wood, true, true) +'</strong></td></tr>' +
								'<tr class="nowrap"><td width="70"><span class="icon header stone"> </span> '+ texts.gui.stone +'</td><td><strong>'+ lib.formatNumber(bootyYesterday.stone, true, true) +'</strong></td></tr>' +
								'<tr class="nowrap"><td width="70"><span class="icon header iron"> </span> '+ texts.gui.iron +'</td><td><strong>'+ lib.formatNumber(bootyYesterday.iron, true, true) +'</strong></td></tr>' +
								'<tr class="nowrap"><td width="70"><span class="icon header ressources"> </span> '+ texts.gui.total +'</td><td><strong>'+ lib.formatNumber(bootyYesterday.sum, true, true) +'</strong></td></tr>' +
							'</table>' +
						'</div>' +
					'</div>';

					var pos = Data.get('booty_position');
					if (pos[0]) jQuery(HTML).insertAfter('#'+ pos[0]);
					else if (pos[1]) jQuery(HTML).insertBefore('#'+ pos[1]);
					// if there is no widget in this column (or the once existing one isn't there any more, e.g. show_secrect)
					else jQuery('#'+ pos[2]).append(HTML);
					if (!__('show_booty')) {
						Data.setStd('booty_position');
						jQuery('#rightcolumn').append(HTML);
					}

					var bootyLink = _T(__('show_booty'), 'a', 0);
					if (!bootyLink)
						return;

					bootyLink.addEventListener('click', function() {
						var HTML = '<div style="min-width: 350px; max-height: '+ Math.floor(window.innerHeight * 0.6) +'px; overflow: auto;"><table class="vis"><tr>' +
							'<th>Datum</th>' +
							'<th style="text-align: center;"><span class="icon header wood"></th>' +
							'<th style="text-align: center;"><span class="icon header stone"></th>' +
							'<th style="text-align: center;"><span class="icon header iron"></th>' +
							'<th style="text-align: center;"><span class="icon header ressources"></th>' +
						'</tr>';
						var list = [];
						var tmpHTML;
						var sum = {
							wood: 0,
							stone: 0,
							iron: 0,
							sum: 0
						};
						var Res = Data.booty.get_data();
						for (var dateKey in Res) {
							if (!dateKey)
								continue;

							var res = {
								wood: Data.booty.get(dateKey, 'wood'),
								stone: Data.booty.get(dateKey, 'stone'),
								iron: Data.booty.get(dateKey, 'iron')
							};
							res.sum = res.wood + res.stone + res.iron;
							if (!res.sum) {
								list.push([dateKey, 0]);
								continue;
							}

							var title = {};
							var values = {};
							for (var key in res) {
								title[key] = lib.formatNumber(res[key], true);
								values[key] = lib.formatNumber(res[key], true, true, 1);
								title[key] = '';
								if (title[key] != values[key])
									title[key] = ' title="'+ title[key] +'"';
								sum[key] += res[key];
							}
							var date = lib.formatTime(lib.formatKey2Time(dateKey).getTime(), 'dd.MM.YY');
							tmpHTML = '<td><i>'+ date +'</i></td>' +
							'<td style="text-align: right;"'+ title.wood +'>'+ values.wood +'</td>' +
							'<td style="text-align: right;"'+ title.stone +'>'+ values.stone +'</td>' +
							'<td style="text-align: right;"'+ title.iron +'>'+ values.iron +'</td>' +
							'<td style="text-align: right;"'+ title.sum +'>'+ values.sum +'</td>';
							list.push([dateKey, tmpHTML]);
						}
						list.sort(function(a, b) {
							return b[0] - a[0];
						});
						var lastEmpty = false;
						var a = 0;
						for (var i = 0, max = list.length; i < max; i++) {
							if (i > 0 && list[i][0] != list[i-1][0]-1 && !lastEmpty) {
								HTML += '<tr class="row_'+ (a % 2 ? 'a' : 'b') +'"><td colspan="5"></td></tr>';
								lastEmpty = true;
								a++;
							}
							if (i > 0 && !list[i][1] && !lastEmpty || list[i][1]) {
								HTML += '<tr class="row_'+ (a % 2 ? 'a' : 'b') +'">'+ (list[i][1] ? list[i][1] : '<td colspan="5"></td>') +'</tr>';
								lastEmpty = !list[i][1];
								a++;
							}
						}

						var title = {};
						var values = {};
						for (var key in sum) {
							title[key] = lib.formatNumber(sum[key], true);
							values[key] = lib.formatNumber(sum[key], true, true, 2);
							if (title[key] == values[key]) title[key] = '';
							else title[key] = ' title="'+ title[key] +'"';
						}
						HTML += '<tr><td colspan="5" style="background-color: grey; height: 1px; padding: 0;"></td></tr>' +
						'<tr class="row_'+ (a % 2 ? 'a' : 'b') +'">' +
						'<td style="text-align: right;"><b>'+ texts.gui.total +':</b></td>' +
						'<td style="text-align: right;"'+ title.wood +'><b>'+ values.wood +'</b></td>' +
						'<td style="text-align: right;"'+ title.stone +'><b>'+ values.stone +'</b></td>' +
						'<td style="text-align: right;"'+ title.iron +'><b>'+ values.iron +'</b></td>' +
						'<td style="text-align: right;"'+ title.sum +'><b>'+ values.sum +'</b></td>' +
						'</tr>';
						HTML += '</table></div>';

						var popup = new lib.Popup({
							id: 'booty',
							title: texts.gui.booty,
							content: HTML,
							shadowDiv: true
						});
						popup = 0;
					}, false);
					jQuery('#show_booty').find('img').last().get(0).addEventListener('click', function() {
						var $element = jQuery('#show_booty').children('div').last();
						$element.toggle();
						this.src = $element.is(':hidden') ? 'graphic/plus.png' : 'graphic/minus.png';
						Data.set('booty_show', !$element.is(':hidden'));
						Data.save();
						return false;
					}, false);
				}
			} else if (screen == 'place') {

				if (__('units_form')) {
					Settings.placeForm();

					var new_report = __('new_report');
					// new report
					if (new_report && new_report.style.display != 'none') {
						var cell = new_report.parentNode.parentNode;
						cell.id = 'new_reportCell';
						var width = cell.offsetWidth;
						Style.newReportAnimation(width);
					}

					if (Data.settings.get('farmlist_option')) {
						// add styles
						Style.farmlist();

						jQuery('#selectAllUnits').parent().parent().append('<td><a href="javascript:;" id="dsplus_insertFarmUnits">'+ lib.guillemetRight +' '+ texts.gui.insertFarmUnits +'</a></td><td><a href="javascript:;" id="dsplus_insertSpyUnits">'+ lib.guillemetRight +' '+ texts.gui.insertSpyUnits +'</a></td>');
						__('dsplus_insertFarmUnits').addEventListener('click', function() {
							var coords = __('inputx').value +'|'+ __('inputy').value;
							var ID = Data.villages.getID(coords);
							if (Data.villages.existSpy(ID))
								Farmlist.clickCoords(coords);
						}, false);
						__('dsplus_insertSpyUnits').addEventListener('click', function() {
							var ID = __('inputx').value +'|'+ __('inputy').value;
							if (/\d{1,3}\|\d{1,3}/.test(ID))
								Farmlist.clickAge(ID);
							else
								Farmlist.clickAge();
						}, false);
						if (Data.settings.get('farmlist_option') == 1)
							jQuery('#target_attack').parent().parent().parent().append('<tr></tr><tr><td colspan="6"><a id="dsplus_farmlistLink" href="javascript:;">'+ lib.guillemetRight +' ' + texts.gui.farmlist + '</td></tr>');
						else if (Data.settings.get('farmlist_option') == 2)
							jQuery('#units_form').after('<table class="vis" style="width: 100%;"><thead><tr><th colspan="20">' + texts.gui.farmlist + ' <a id="dsplus_farmlistLink" href="javascript:;" class="float_right">' + (!Data.settings.get('farmlistState') ? lib.guiDown : lib.guiUp) + '</a></th></tr></thead></table>');
						__('dsplus_farmlistLink').addEventListener('click', function() {
							if (Data.settings.get('farmlist_option') == 1) {
								Farmlist.load();
							} else if (Data.settings.get('farmlist_option') == 2) {
								if (!Data.settings.get('farmlistState')) {
									Data.settings.set('farmlistState', 1);
									Data.settings.save();
									__('dsplus_farmlistLink').innerHTML = lib.guiUp;
									Farmlist.load();
								} else {
									Data.settings.set('farmlistState', 0);
									Data.settings.save();
									__('dsplus_farmlistLink').innerHTML = lib.guiDown;
									_remove(__('dsplus_farmTable'));
								}
							}
							this.blur();
						}, false);
						if (Data.settings.get('farmlist_option') == 2 && Data.settings.get('farmlistState') == 1) {
							Farmlist.load();

							__('inputx').addEventListener('keyup', Farmlist.changeCoords, false);
							__('inputy').addEventListener('keyup', Farmlist.changeCoords, false);
						}
					}

					setTimeout(function() {
						if (lib.storage.getValue('place_units')) {
							var units = lib.storage.getValue('place_units');
							for (var unit in units) {
								var num = units[unit];
								__('unit_input_'+ unit).value = num;
							}
							lib.storage.deleteValue('place_units');
						} else if (lib.storage.getValue('place_all')) {
							win.selectAllUnits(true);
							lib.storage.deleteValue('place_all');
						}
					});
				} else if (__('troop_confirm_go') && _T(content_value, 'a').length) {
					var ID;
					var idx = 0;
					var links = _T(content_value, 'a');
					for (var i = 0, link; (link = links[i]) && !ID; i++) {
						var match = link.href && link.href.match(regex.id);
						if (match) {
							ID = match[1];
							idx = i;
						}
					}
					if (ID) {
						var ownerID = 0;
						for (var i = idx+1, link; (link = links[i]) && !ownerID; i++) {
							var match = link.href && link.href.match(regex.id);
							if (match) {
								ownerID = match[1];
								idx = i;
							}
						}
						if (Data.villages.existSpy(ID) && ownerID && ownerID != Data.villages.getOwnerID(ID)) {
							links[idx].parentNode.style.backgroundColor = 'red';
							__('troop_confirm_go').disabled = true;
							window.setTimeout(function() {
								__('troop_confirm_go').disabled = false;
							}, 600);
						}

						/*
						 * Farmlist.init();
						 */
						if (Data.settings.get('farming_sendCatas')) {
							var rows = doc.querySelector('td.unit-item').parentNode.parentNode.rows;
							var thRow = rows[0];
							var ramCount = 0;
							var cataCount = 0;
							var imgs = _T(rows[0], 'img');
							var cells = rows[1].cells;
							for (var i = 0, img; img = imgs[i]; i++) {
								if (/unit_ram\.png/.test(img.src)) {
									ramCount = parseInt(cells[i].innerHTML, 10);
								} else if (/unit_catapult\.png/.test(img.src)) {
									cataCount = parseInt(cells[i].innerHTML, 10);
								}
							}
							if (cataCount) {
								var building_order = Data.settings.get('farming_buildingPreferences_order');
								var buildingPreferences = Data.settings.get('farming_buildingPreferences');
								for (var i = 0, build; build = building_order[i]; i++) {
									if (!buildingPreferences[build])
										continue;
									var level = Data.villages.getBuilding(ID, build);
									if (level && (build != 'wall' || ramCount < lib.ramsNeeded.destroy[level])) {
										doc.querySelector('select[name="building"]').value = build;
										i = -2;
									}
								}
							}
						}
					}
				} else if (game_data.mode == 'sim') {
					var attackerUnits = lib.storage.getValue('sim_units_attacker');
					var defenderUnits = lib.storage.getValue('sim_units_defender');
					var wall = lib.storage.getValue('sim_wall');
					if (attackerUnits && defenderUnits) {
						for (var unit in attackerUnits) {
							var input = doc.querySelector('input[name="att_'+ unit +'"]');
							if (input)
								input.value = attackerUnits[unit];
						}
						for (var unit in defenderUnits) {
							var input = doc.querySelector('input[name="def_'+ unit +'"]');
							if (input)
								input.value = defenderUnits[unit];
						}
						var wall_input = doc.querySelector('input[name="def_wall"]');
						if (wall_input && wall)
							wall_input.value = wall;
					}
					lib.storage.deleteValue('sim_units_attacker');
					lib.storage.deleteValue('sim_units_defender');
					lib.storage.deleteValue('sim_wall');
				} else if (game_data.mode == 'call') {
					var HTML = '<tr>';
					jQuery('#support_sum').find('tr').eq(0).find('img').each(function() {
						var match = jQuery(this).attr('src').match(regex.unitKey);
						var unit = match[1];
						HTML += '<td><input type="text" id="dsplus_'+ unit +'" size="5" value="'+ (PA_Features.call_units[unit] ? PA_Features.call_units[unit] : 0) +'"></td>';
					});
					HTML += '</tr>';
					jQuery('#support_sum').append(HTML)
					.find('input').each(function() {
						this.addEventListener('keyup', function() {
							var unit = this.id.match(/dsplus_(.+)/)[1];
							PA_Features.call_units[unit] = this.value;
							lib.storage.setValue('call_units', PA_Features.call_units);
						}, false);
					});
					jQuery('<div style="text-align: right;"><input type="button" value="'+ texts.gui.insert +'"></div>').insertAfter('#support_sum')
					.find('input').get(0).addEventListener('click', PA_Features.call_clickInsert, true);

					/*
					 * Thanks to bassem-h-1234 for this code:
					 */
					// Select all
					jQuery('#village_troup_list').find('tr').find('th').eq(0).html('<button id="dsfa_all">'+ texts.gui.selectAll +'</button>');
					jQuery('#dsfa_all').click(function() {
						jQuery('#village_troup_list').find('tr').slice(1).each(function() {
							// there is only the button "Select"
							if (jQuery(this).find('input').length == 1 && !jQuery(this).find('input').attr('disabled'))
								jQuery(this).find('input.call_button').click();
						});
					});
					// Call all
					jQuery('#village_troup_list').find('tr').find('th').last().html('<button id="dsfa_call" class="tooltip" title="'+ texts.gui.callAllTitle +'" style="font-size: 96%;">'+ texts.gui.callAll +'</button>');
					jQuery('#dsfa_call').click(function() {
						jQuery('#village_troup_list').find('tr').slice(1).each(function() {
							// there are also other inputs than only the button -> the button is a "Call"-button
							if (jQuery(this).find('input').length > 1 && !jQuery(this).find('input').last().attr('disabled'))
								jQuery(this).find('input.call_button').click();
						});
					});

				}
			} else if (screen == 'main') {
				Style.buildingRow();
			} else if (screen == 'market') {
				Storage.colorize = Data.settings.get('storageColor_enabledAt', 'market');
				document.addEventListener('DOMNodeInserted', function(e) {
					// the "own"-popup is now open
					if (e.relatedNode.id == 'inline_popup_content') {
						var runtime_market = Runtime();
						Storage.init_market_own();
						Storage.colorRes();
						runtime_market.log('Runtime (market-DOMNodeInserted)');
					} else
					// "arrival of the last merchant" has changed (on the call page)
					if (e.relatedNode.id == 'arrive') {
						Storage.init_market_call();
						Storage.colorRes();
					}
				}, false);

				if (game_data.mode == 'call') {
					Storage.init_market_call();

					var HTML = '<tr>';
					jQuery('#res_sum').find('td').each(function() {
						var res = jQuery(this).attr('id').replace('total_', '');
						HTML += '<td><input type="text" id="dsplus_call_'+ res +'" size="5" value="'+ (PA_Features.call_res[res] ? PA_Features.call_res[res] : 0) +'"></td>';
					});
					HTML += '</tr>';
					jQuery('#res_sum').append(HTML)
					.find('input').each(function() {
						this.addEventListener('keyup', function() {
							var res = this.id.replace('dsplus_call_', '');
							PA_Features.call_res[res] = this.value;
							lib.storage.setValue('call_res', PA_Features.call_res);
						}, false);
					});
					jQuery('#res_sum').find('tr').eq(1).parent().append(
						'<tr><td style="text-align: right;" colspan="3">' +
							'<input type="button" value="'+ texts.gui.insert +'">' +
						'</td></tr>'
					).find('input').last().get(0).addEventListener('click', PA_Features.call_res_clickInsert, true);


					/*
					 * Thanks to bassem-h-1234 for this code:
					 */
					// Select all
					jQuery('#village_list').find('tr').find('th').eq(0).html('<button id="dsfa_all">'+ texts.gui.selectAll +'</button>');
					jQuery('#dsfa_all').click(function() {
						jQuery('#village_list').find('tr').slice(1).each(function() {
							// there is only the button "Select"
							if (jQuery(this).find('input').length == 1 && !jQuery(this).find('input').attr('disabled'))
								jQuery(this).find('input.call_button').click();
						});
					});
					// Call all
					jQuery('#village_list').find('tr').find('th').last().html('<button id="dsfa_call" class="tooltip" title="'+ texts.gui.callAllTitle +'" style="font-size: 96%;">'+ texts.gui.callAll +'</button>');
					jQuery('#dsfa_call').click(function() {
						jQuery('#village_list').find('tr').slice(1).each(function() {
							// there are also other inputs than only the button -> the button is a "Call"-button
							if (jQuery(this).find('input').length > 1 && !jQuery(this).find('input').last().attr('disabled'))
								jQuery(this).find('input.call_button').click();
						});
					});
				}
			} else if (screen == 'map') {
				/*
				 * Map.init();
				 */
				if (__('map')) {
					/*
					 * using the Mutation Observer:
					 */
					var mapObserver = new MutationObserver(function(mutations) {
						for (var i = 0, max = mutations.length; i < max; i++) {
							var nodes = mutations[i].addedNodes;
							if (!nodes.length)
								continue;

							for (var k = 0, len = nodes.length; k < len; k++)
								Map.updateMap({ target: nodes[k] });
						}
					});
					var config = { attributes: true, childList: true, characterData: true, subtree: true };
					var attachMapEvent = function(imgContainer) {
						if (!imgContainer.querySelector('div.dsplus_Info_overlay')) {
							var imgs = _T(imgContainer, 'img');
							for (var i = 0, img; img = imgs[i]; i++) {
								Map.updateMap({ target: img });
							}
						}
						mapObserver.observe(imgContainer, config);
					};
					if (__('map_container')) {
						attachMapEvent(__('map_container'));
					} else {
						// after "map_container" has beend inserted, DOMNodeInserted can be listened by Map.updateMap
						attachMapEvent(__('map'));
					}

				}

				var map_config = __('map_config');
				var tpl_popup = __('tpl_popup');
				if (tpl_popup) {
					var HTML = tpl_popup.innerHTML
					.replace('<% } /* end points */ %>', '<% } /* end points */ %>' +
					'<% if (dsplus.mood < 100) { %>' +
						'<tr id="dsplus_mood">' +
							'<td>'+ texts.gui.mood +':</td>' +
							'<td><%= dsplus.mood %></td>' +
						'</tr>' +
					'<% } /* end mood */ %>')
					.replace('<% } /* end ally */ %>', '<% } /* end ally */ %>' +
					'<% if (dsplus.template.building) { %>' +
						'<tr>' +
							'<td>'+ texts.gui.mapPopupOptions.buildTemplate +':</td>' +
							'<td><%= dsplus.template.building %></td>' +
						'</tr>' +
					'<% } /* end building template */ %>' +
					'<% if (dsplus.template.unit) { %>' +
						'<tr>' +
							'<td>'+ texts.gui.mapPopupOptions.unitTemplate +':</td>' +
							'<td><%= dsplus.template.unit %></td>' +
						'</tr>' +
					'<% } /* end unit template */ %>')
					.replace(/%>\s*<\/table>\s*$/, '%>' +
					'<% if (dsplus.res) { %>' +
						'<tr id="dsplus_contingentRes">' +
							'<td class="nowrap">'+ texts.gui.contingentRes +':</td>' +
							'<td style="padding: 0;">' +
								'<table class="overview_table" style="border-spacing: 6px 0;"><tr>' +
									'<td style="padding: 2px 3px;" class="wood"><span class="<%= dsplus.resClass.wood %> wood"><%= dsplus.res.wood %></span></td>' +
									'<td style="padding: 2px 3px;" class="stone"><span class="<%= dsplus.resClass.stone %> stone"><%= dsplus.res.stone %></span></td>' +
									'<td style="padding: 2px 3px;" class="iron"><span class="<%= dsplus.resClass.iron %> iron"><%= dsplus.res.iron %></span></td>' +
									'<td style="padding: 2px 3px;"><span style="padding: 0 10px 0 0;">/</span><span style="background: transparent url('+ image_base +'res.png?) no-repeat;" class="res" title="'+ texts.gui.effectiveStorage +'"><%= dsplus.res.max %></span></td>' +
								'</tr></table>' +
							'</td>' +
						'</tr>' +
					'<% } %>' +
					'<% if (dsplus.buildings) { %>' +
						'<tr id="dsplus_buildings">' +
							'<td class="nowrap">'+ texts.gui.buildings +':<br><span style="font-size: 80%; font-style: italic;"><%= dsplus.spyDate %></span></td>' +
							'<td>' +
								'<table id="buildings">' +
									'<tr><%= dsplus.buildings[0] %></tr>' +
									'<tr><%= dsplus.buildings[1] %></tr>' +
									'<tr><%= dsplus.buildings[2] %></tr>' +
								'</table>' +
							'</td>' +
						'</tr>' +
					'<% } %>' +
					'<% if (dsplus.units) { %>' +
						'<tr id="dsplus_units">' +
							'<td class="nowrap">'+ texts.gui.units +':<br><span style="font-size: 80%; font-style: italic;"><%= dsplus.spyDate %></span></td>' +
							'<td>' +
								'<table id="units">' +
									'<tr><%= dsplus.units[0] %></tr>' +
									'<tr><%= dsplus.units[1] %></tr>' +
								'</table>' +
							'</td>' +
						'</tr>' +
					'<% } %>' +
					'</table>');
					tpl_popup.innerHTML = HTML;
				}

				var optionsForm = __('form_map_popup');
				if (!lib.hasPA) {
					var lastRow = _T(map_config, 'tr', 'last');
					var lastCell = _T(map_config, 'td', 'last');
					if (lastRow && lastCell) {
						var optionsToggler = _ce('img', '', {
							'src': image_base +'icons/slide_'+ (Map.popup_options.options_open ? 'up' : 'down') +'.png',
							'class': 'dsplus_popup_options_toggler popup_options_toggler'
						});
						optionsToggler.addEventListener('click', function() {
							Map.popup_options.options_open = !Map.popup_options.options_open;
							Data.set('map_popup_options', 'options_open', Map.popup_options.options_open);
							Data.save();

							this.src = image_base +'icons/slide_'+ (Map.popup_options.options_open ? 'up' : 'down') +'.png';

							jQuery(optionsRow).toggle('fast');
						}, true);
						lastCell.appendChild(optionsToggler);

						var optionsRow = _ce(
							'tr',
							'<td style="padding-left:8px" colspan="3">' +
								'<form id="dsplus_form_map_popup">' +
									'<table></table>' +
								'</form>' +
							'</td>',
							{
								'id': 'dsplus_popup_options'
							}
						);
						if (!Map.popup_options.options_open)
							_hide(optionsRow);
						lastRow.parentNode.appendChild(optionsRow);

						optionsForm = __('dsplus_form_map_popup');
					}
				}

				var options = {
					buildTemplate: {
						isPAFeature: false,
						disabled: false,
						checked: Map.popup_options.buildTemplate
					},
					unitTemplate: {
						isPAFeature: false,
						disabled: false,
						checked: Map.popup_options.unitTemplate
					},
					attack: {
						isPAFeature: true,
						disabled: true,
						checked: Map.popup_options.attack
					},
					attack_intel: {
						isPAFeature: true,
						disabled: true,
						checked: Map.popup_options.attack_intel
					},
					moral: {
						isPAFeature: true,
						disabled: true,
						checked: Map.popup_options.moral
					},
					res: {
						isPAFeature: true,
						disabled: true,
						checked: Map.popup_options.res
					},
					pop: {
						isPAFeature: true,
						disabled: true,
						checked: Map.popup_options.pop
					},
					trader: {
						isPAFeature: true,
						disabled: true,
						checked: Map.popup_options.trader
					},
					reservation: {
						isPAFeature: true,
						disabled: true,
						checked: Map.popup_options.reservation
					},
					units: {
						isPAFeature: true,
						disabled: true,
						checked: Map.popup_options.units,
						onclick: '$("#map_popup_units_home").attr("disabled", this.checked ? "" : "disabled").attr("checked", "")'
					},
					units_home: {
						isPAFeature: true,
						disabled: true,
						checked: Map.popup_options.units_home
					},
					units_times: {
						isPAFeature: true,
						disabled: false,
						checked: Map.popup_options.units_times
					},
					notes: {
						isPAFeature: true,
						disabled: true,
						checked: Map.popup_options.notes
					},
					farmInfos: {
						isPAFeature: false,
						disabled: false,
						checked: Map.popup_options.farmInfos
					}
				};
				var table = _T(optionsForm, 'table', 0);
				var clickFn = function() {
					if (this.disabled)
						return;
					var optionName = this.id.replace('map_popup_', '');
					Map.popup_options[optionName] = this.checked;
					Data.set('map_popup_options', optionName, Map.popup_options[optionName]);
					Data.save();
				};
				var simulatePA = !lib.hasPA && Data.settings.get('freePA');
				var lastElement;
				for (var optionName in options) {
					var option = options[optionName];
					if (option.isPAFeature && !simulatePA || option.disabled) {
						var checkbox = __('map_popup_'+ optionName);
						if (checkbox)
							lastElement = checkbox.parentNode.parentNode;
						continue;
					}

					var row = table.insertRow(0);
					if (lastElement)
						_after(row, lastElement);
					lastElement = row;
					var checkboxCell = row.insertCell(-1);
					var checkbox = _ce('input', '', {
						'id': 'map_popup_'+ optionName,
						'type': 'checkbox'
					});
					checkbox.checked = option.checked;
					if (option.onclick)
						checkbox.setAttribute('onclick', option.onclick);
					checkbox.addEventListener('click', clickFn, false);
					checkboxCell.appendChild(checkbox);
					if (lib.hasPA) {
						lib.executeFnInUnsafewindow(function(optionName) {
							setTimeout(function() {
								jQuery('#map_popup_'+ optionName).off();
							}, 10);
						}, [optionName]);
					}

					var labelCell = row.insertCell(-1);
					var label = _ce('label', texts.gui.mapPopupOptions[optionName] +' <span class="grey">('+ texts.gui.DSPlus +')</span>', {
						'for': 'map_popup_'+ optionName
					});
					labelCell.appendChild(label);
				}

				// make jQuery it available!
				var $ = jQuery;
				// creating the new popup-function
				var displayForVillageStr = win.TWMap.popup.displayForVillage.toString();
				// correct all references (TWMap -> win.TWMap, this -> win.TWMap.popup)
				displayForVillageStr = displayForVillageStr.replace(/TWMap\./g, 'win.TWMap.').replace(/this\./g, 'win.TWMap.popup.');
				// get the String of the add-on-Code
				var addOnStr = Map.displayForVillageAddOn.toString().replace(/function.+{\s*(["']use strict["'];?)?\s*|\s*}$|\/\*.*\*\//g, '');
				// insert the add-on-Code into the original function
				displayForVillageStr = displayForVillageStr.replace('$(\'#map_popup\').html(jstpl(\'tpl_popup\',dat))', addOnStr);
				// try to parse this new function
				try {
					eval('Map.displayForVillage = '+ displayForVillageStr +';');
				} catch(error) {
					lib.debug.log('DS+: Error while parsing Map.displayForVillage');
					throw error;
				}
				// replace the old function with the new one
				win.TWMap.popup.displayForVillage = Map.displayForVillage;

				Style.map();

				var map_config_lastTable = map_config.children[map_config.children.length-1];
				if (Data.settings.get('map_overlays_enabled')) {

					var HTML = '<table class="vis" style="width: 100%; margin: 0;">' +
						'<tr>' +
							'<td class="nowrap" style="width:1px">'+ texts.gui.mapOverlays.own +':</td>' +
							'<td><select id="dsplus_ownOvl">';
					var ownOptions = ['none', 'points', 'name', 'coords', 'def'];
					for (var i = 0, key; key = ownOptions[i]; i++) {
						HTML += '<option value="'+ key +'"'+ (Map.ownOvl == key ? ' selected' : '') +'>'+ texts.gui.mapOverlays[key] +'</option>';
					}
					HTML += '</select></td>' +
						'</tr>' +
						'<tr>' +
							'<td class="nowrap">'+ texts.gui.mapOverlays.other +':</td>' +
							'<td><select id="dsplus_otherOvl">';
					var otherOptions = ['none', 'points', 'name', 'coords', 'eq'];
					for (var i = 0, key; key = otherOptions[i]; i++) {
						HTML += '<option value="'+ key +'"'+ (Map.otherOvl == key ? ' selected' : '') +'>'+ texts.gui.mapOverlays[key] +'</option>';
					}
					HTML += '</select></td>' +
						'</tr>' +
					'</table>';

					var overlayRow = map_config_lastTable.insertRow(-1);
					var overlayCell = overlayRow.insertCell(-1);
					overlayCell.colSpan = 3;
					overlayCell.style.padding = 0;
					overlayCell.style.border = 0;
					overlayCell.innerHTML = HTML;

					__('dsplus_ownOvl').addEventListener('change', Map.changeSelectedOverlay);
					__('dsplus_otherOvl').addEventListener('change', Map.changeSelectedOverlay);
				}

				if (!lib.hasPA && Data.settings.get('freePA')) {
					if (win.TWMap.resizeMinimap) {
						win.TWMap.premium = true;
						win.TWMap.urls.sizeSave = '';
						_show(__('fullscreen'));

						var mapResizeInterval = window.setInterval(function() {
							if (win.TWMap) {
								win.TWMap.resize(Map.mapSize);
								win.TWMap.resizeMinimap(Map.minimapSize);
								// wieder richtig zentrieren
								win.TWMap.focusSubmit();
								window.clearInterval(mapResizeInterval);
								mapResizeInterval = null;
							}
						}, 50);

						var table = _ce('table', '', {
							'width': '100%',
							'class': 'vis'
						});
						__('map_topo').appendChild(table);

						table.insertRow(-1).innerHTML = '<th colspan="2">'+ texts.gui.freePA.map_changeSize +'</th>';
						var mapSizeCell = table.insertRow(-1).insertCell(-1);
						var HTML = '<table cellspacing="0"><tr>' +
							'<td width="80">'+ texts.gui.freePA.map +':</td>' +
							'<td><select id="map_chooser_select">';
						for (var i = 0, size; size = Map.map_sizes[i]; i++) {
							HTML += '<option value="'+ size +'"'+ (size == Map.mapSize ? ' selected' : '') +'>'+ size +'x'+ size +'</option>';
						}
						HTML += '</select></td>' +
							'<td valign="middle"><img alt="" class="tooltip" title="'+ texts.gui.freePA.map_questionmark +'" src="graphic/questionmark.png" width="13" height="13"></td>' +
						'</tr></table>';
						mapSizeCell.innerHTML = HTML;
						__('map_chooser_select').addEventListener('change', function() {
							Map.mapSize = parseInt(__('map_chooser_select').value, 10);
							Data.set('mapSize', Map.mapSize);
							Data.save();
							win.TWMap.resize(Map.mapSize, true);
						}, false);

						var minimapSizeCell = table.insertRow(-1).insertCell(-1);
						var HTML = '<table cellspacing="0"><tr>' +
							'<td width="80">'+ texts.gui.freePA.minimap +':</td>' +
							'<td colspan="2"><select id="minimap_chooser_select">';
						for (var i = 0, size; size = Map.minimap_sizes[i]; i++) {
							HTML += '<option value="'+ size +'"'+ (size == Map.minimapSize ? ' selected' : '') +'>'+ size +'x'+ size +'</option>';
						}
						HTML += '</select></td>' +
						'</tr></table>';
						minimapSizeCell.innerHTML = HTML;
						__('minimap_chooser_select').addEventListener('change', function() {
							var pos = location.href.match(/#(\d+);(\d+)/);
							Map.minimapSize = parseInt(__('minimap_chooser_select').value, 10);
							Data.set('minimapSize', Map.minimapSize);
							Data.save();
							win.TWMap.resizeMinimap(Map.minimapSize, true);
							win.TWMap.map.centerPos(pos[1], pos[2], true);
						}, false);

						// bisher noch ohne Funktion:
						var mapResizer = _ce('div', '', {
							'class': 'ui-resizable-handle ui-resizable-se ui-icon ui-icon-gripsmall-diagonal-se',
							'style': 'z-index: 14;'
						});
						__('map').appendChild(mapResizer);

						// bisher noch ohne Funktion:
						var minimapResizer = _ce('div', '', {
							'class': 'ui-resizable-handle ui-resizable-se ui-icon ui-icon-gripsmall-diagonal-se',
							'style': 'z-index: 14;'
						});
						__('minimap').appendChild(minimapResizer);
					}

				}
			} else if (screen == 'snob') {
				if (game_data.mode == 'coin') {
					Storage.colorize = Data.settings.get('storageColor_enabledAt', 'coin_mint');
					Storage.init_coin_mint();
					document.addEventListener('DOMNodeInserted', function(e) {
						if (e.relatedNode.id == 'selectedBunches_top') {
							Storage.init_coin_mint();
							Storage.colorRes();
						}
					}, false);
				}
			} else if (screen == 'report') {
				/*
				 * Report.init();
				 */
				// hier Bericht-Daten (eingelesen, oder nicht, alter etc.) auslesen, bearbeiten...
				Report.isPublic = regex.publicReport.test(location.href);
				if (regex.viewId.test(location.href) || Report.isPublic) {
					var newIds = false;
					var elementInTable = __('label');
					if (!elementInTable) {
						elementInTable = _C(doc, 'quickedit-content', 0).parentNode;
						if (!elementInTable) {
							elementInTable = _T(__('content_value'), 'h3', 0);
						} else {
							newIds = true;
						}
					}
					var tBody = elementInTable.parentNode.parentNode.parentNode;
					// The read-algorithm is written for this kind of report-structure with 3 rows:
					if ((Report.isPublic || tBody.rows.length >= 3) && !__('reorderContainer')) {
						Style.report();

						// elements containing the information name, dot-type and time
						var nameCell, dotImg, timeCell;
						if (Report.isPublic) {
							nameCell = elementInTable;
							timeCell = _T(tBody, 'h4', 0);
						} else {
							if (newIds) {
								nameCell = _C(doc, 'quickedit-label', 0);
								dotImg = _prev(_C(doc, 'quickedit', 0));
							} else {
								nameCell = __('labelText');
								dotImg = _prev(__('label'));
							}
							timeCell = tBody.rows[1].cells[1];
						}
						// cell containing the report data
						var reportCell = tBody.rows[tBody.rows.length-1].cells[0];

						// create a container-div
						var container = _ce('div', '', { 'class': 'container' });
						var luckContainer, moralContainer, attackerInfoContainer, defenderInfoContainer, spyContainer, units_outContainer, units_wayContainer, attack_resultsContainer;

						var luck = __('attack_luck');
						var moral;
						if (luck) {
							var luckHeader = _prev(luck);
							moral = _next(luck);

							// luck
							luckContainer = container.cloneNode();
							luckContainer.appendChild(luckHeader);
							luckContainer.appendChild(luck);

							// moral
							if (moral && moral.tagName == 'H4') {
								moralContainer = container.cloneNode();
								moralContainer.appendChild(moral);
							}
						}

						// attacker + units
						var attackerInfo = __('attack_info_att');
						if (attackerInfo) {
							var linebreak = _next(attackerInfo);
							attackerInfoContainer = container.cloneNode();
							_before(attackerInfoContainer, attackerInfo);
							attackerInfoContainer.appendChild(attackerInfo);
							if (linebreak && linebreak.tagName == 'BR')
								_remove(linebreak);
						}

						// defender + units
						var defenderInfo = __('attack_info_def');
						if (defenderInfo) {
							var linebreak = _next(defenderInfo);
							defenderInfoContainer = container.cloneNode();
							_before(defenderInfoContainer, defenderInfo);
							defenderInfoContainer.appendChild(defenderInfo);
							if (linebreak && linebreak.tagName == 'BR')
								_remove(linebreak);
						}

						// espionage
						var attack_spy = __('attack_spy');
						var units_outTable;
						if (attack_spy) {
							var headline = _prev(attack_spy);
							var linebreak = _next(attack_spy);
							spyContainer = container.cloneNode();
							_before(spyContainer, headline);
							spyContainer.appendChild(headline);
							spyContainer.appendChild(attack_spy);

							// Defender's troops, that were outside
							if (attack_spy.querySelector('td.unit-item')) {
								units_outTable = attack_spy.cloneNode(false);
								units_outTable.id = 'units_out';
								var rows = attack_spy.rows;
								units_outTable.appendChild(rows[rows.length-2]);
								units_outTable.appendChild(rows[rows.length-1]);

								units_outContainer = container.cloneNode();
								_after(units_outContainer, spyContainer);
								units_outContainer.appendChild(units_outTable);
							}
							if (linebreak && linebreak.tagName == 'BR')
								_remove(linebreak);
						}

						var attack_results = __('attack_results');
						// Defender's troops, that were in transit
						var units_wayTable;
						if (attack_results) {
							units_wayTable = _prev(attack_results);
							if (units_wayTable && !units_wayTable.id && !units_wayTable.classList.contains('container')) {
								var headline = _prev(units_wayTable);
								if (headline && headline.tagName == 'H4') {
									units_wayContainer = container.cloneNode();
									_before(units_wayContainer, headline);
									units_wayContainer.appendChild(headline);
									units_wayContainer.appendChild(units_wayTable);
								}
							} else {
								units_wayTable = null;
							}

							var linebreak = _next(attack_results);
							attack_resultsContainer = container.cloneNode();
							_before(attack_resultsContainer, attack_results);
							attack_resultsContainer.appendChild(attack_results);
							if (linebreak && linebreak.tagName == 'BR')
								_remove(linebreak);
						}

						Report.els = {
							name: nameCell,
							dot: dotImg,
							time: timeCell,
							luck: luck,
							moral: moral,
							attacker: attackerInfo,
							defender: defenderInfo,
							spy: attack_spy,
							units_out: units_outTable,
							units_way: units_wayTable,
							result: attack_results
						};
						Report.containers = {
							luck: luckContainer,
							moral: moralContainer,
							attacker: attackerInfoContainer,
							defender: defenderInfoContainer,
							spy: spyContainer,
							units_out: units_outContainer,
							units_way: units_wayContainer,
							result: attack_resultsContainer
						};
						if (Report.els.luck || Report.els.moral) {
							var afterEl;
							if (Report.isPublic) {
								afterEl = reportCell.children[2];
							} else {
								afterEl = reportCell.children[0];
							}

							// report image
							var report_image = reportCell.querySelector('div.report_image');
							if (report_image)
								_hide(report_image);

							var reorderContainer = _ce('div');
							reorderContainer.id = 'reorderContainer';
							_after(reorderContainer, afterEl);
							if (luckContainer)
								reorderContainer.appendChild(luckContainer);
							if (moralContainer)
								reorderContainer.appendChild(moralContainer);
							if (attackerInfoContainer)
								reorderContainer.appendChild(attackerInfoContainer);
							if (defenderInfoContainer)
								reorderContainer.appendChild(defenderInfoContainer);
							if (spyContainer)
								reorderContainer.appendChild(spyContainer);
							if (units_outContainer)
								reorderContainer.appendChild(units_outContainer);
							if (units_wayContainer)
								reorderContainer.appendChild(units_wayContainer);
							if (attack_resultsContainer)
								reorderContainer.appendChild(attack_resultsContainer);

							// add a line break
							var linebreak = _ce('br');
							_after(linebreak, reorderContainer);
						}

						// analyse the data!
						Report.analyse();

						// now work with that report-data
						if (!Report.isPublic)
							Report.read();
						timeCell.style.fontWeight = 700;
						var color;
						if (Report.status == 3) {
							// dyeing the time cell
							timeCell.style.color = '#B40000';
							color = '#F4B48C';
						} else {
							if (Report.status == 1) {
								color = '#C8E490';
								timeCell.style.color = 'green';
							}
							timeCell.style.background = 'url("'+ image_base +'quests/check.png") no-repeat scroll right center transparent';
						}
						if (color) {
							reportCell.style.backgroundColor = color;
							setTimeout(function() {
								reportCell.style.transition = 'background-color 8s ease-in-out';
								reportCell.style.backgroundColor = '';
							});
						}

						if (Report.isAttack) {
							if (Data.settings.get('reports_reorder')) {
								GUI.reports.initReorder(reportCell, Report.containers);
							}

							if (Data.settings.get('reports_showSurvivor')) {
								Report.addSurvivorRow();
							}
							if (Data.settings.get('reports_showBashPoints')) {
								Report.addBashPointRow();
							}

							var line = _T(reportCell, 'hr', 0);
							if (!lib.hasPA && Data.settings.get('freePA') && Report.targetID && line) {
								var sim_units = _ce('a', lib.guillemetRight +' '+ texts.gui.freePA.report.sim_units, {
									'href': lib.createHref('place', 'mode', 'sim')
								});
								sim_units.addEventListener('mouseup', function(e) {
									// attacking units
									lib.storage.setValue('sim_units_attacker', Report.attackerUnits.num);

									// defending units
									lib.storage.setValue('sim_units_defender', Report.defenderUnits.num);

									// wall
									var wall = Report.buildings.wall || Report.result.damage.wall || '';
									lib.storage.setValue('sim_wall', wall);
								}, false);
								_before(sim_units, line);
								_before(_ce('br'), line);

								var sim_only_survivor = _ce('a', lib.guillemetRight +' '+ texts.gui.freePA.report.sim_only_survivor, {
									'href': lib.createHref('place', 'mode', 'sim')
								});
								sim_only_survivor.addEventListener('mouseup', function() {
									// attacking units
									var attackerUnits = {};
									for (var unit in Report.attackerUnits.num) {
										var num = Report.attackerUnits.num[unit];
										var loss = Report.attackerUnits.loss[unit] || 0;
										var alive = num - loss;
										if (alive) attackerUnits[unit] = alive;
									}
									lib.storage.setValue('sim_units_attacker', attackerUnits);

									// defending units
									var defenderUnits = {};
									for (var unit in Report.defenderUnits.num) {
										var num = Report.defenderUnits.num[unit];
										var loss = Report.defenderUnits.loss[unit] || 0;
										var alive = num - loss;
										if (alive) defenderUnits[unit] = alive;
									}
									lib.storage.setValue('sim_units_defender', defenderUnits);

									// wall
									var wall = Report.buildings.wall || Report.result.damage.wall || '';
									lib.storage.setValue('sim_wall', wall);
								}, false);
								_before(sim_only_survivor, line);
								_before(_ce('hr'), line);

								var targetLink = lib.createHref('place', 'target', Report.targetID);
								var attach_this = _ce('a', lib.guillemetRight +' '+ texts.gui.freePA.report.attack_this, {
									'href': targetLink
								});
								_before(attach_this, line);
								_before(_ce('br'), line);

								var place_same = _ce('a', lib.guillemetRight +' '+ texts.gui.freePA.report.place_same, {
									'href': targetLink
								});
								place_same.addEventListener('mouseup', function() {
									lib.storage.setValue('place_units', Report.attackerUnits.num);
								}, false);
								_before(place_same, line);
								_before(_ce('br'), line);

								var place_all = _ce('a', lib.guillemetRight +' '+ texts.gui.freePA.report.place_all, {
									'href': targetLink
								});
								place_all.addEventListener('mouseup', function() {
									lib.storage.setValue('place_all', true);
								}, false);
								_before(place_all, line);
								_before(_ce('br'), line);
							}
						}

						// add the 'Scan' link to the modemenu
						var modemenu = _C(document, 'modemenu', 0);
						if (modemenu) {
							var row = modemenu.insertRow(-1);
							var cell = row.insertCell(-1);
							cell.width = '100';

							var hotkeyChar = Data.settings.get('hotkeys_enabled') ? ' ['+ Hotkeys.char(82) +']' : '';
							var readLink = _ce('a', texts.gui.reportReading + hotkeyChar, {
								'href': 'javascript:;'
							});
							readLink.addEventListener('click', Bot.startReportReading, true);
							cell.appendChild(readLink);
						}
					}
				} else if (__('report_list')) {
					Bot.insertReportButtons();

					// insert some buttons
					var delButton = document.querySelector('input[name="del"]');
					if (delButton) {
						delButton.addEventListener('click', function(e) {
							var rows = document.getElementById('report_list').rows;
							for (var i = 1, max = rows.length-1; i < max; i++) {
								var checkbox = rows[i].querySelector('input[type="checkbox"]');
								if (checkbox && checkbox.checked) {
									// delete Data from Data.reports ...
									var ID = parseInt(checkbox.name.replace('id_', ''), 10);
									if (!isNaN(ID))
										Data.reports.remove(ID);
								}
							}
							Data.reports.save(true);
						}, false);

						var cell = delButton.parentNode;
						// insert Forwarding-Buttons
						if (!lib.hasPA && Data.settings.get('freePA')) {
							var input = _ce('input', '', {
								'type': 'submit',
								'class': 'btn',
								'name': 'forward',
								'value': texts.gui.freePA.report.forward
							});
							cell.appendChild(input);
							var input = _ce('input', '', {
								'type': 'submit',
								'class': 'btn',
								'name': 'real_forward',
								'value': texts.gui.freePA.report.real_forward
							});
							cell.appendChild(input);
						}
						// insert "mark old reports"-button
						var newCell = cell.parentNode.insertCell(-1);
						var dsplus_markOldReports = _ce('input', '', {
							'id': 'dsplus_markOldReports',
							'class': 'btn',
							'type': 'button',
							'value': texts.gui.markOldReports
						});
						newCell.appendChild(dsplus_markOldReports);
						dsplus_markOldReports.addEventListener('click', function() {
							// mark all old reports
							var rows = __('report_list').rows;
							for (var i = 1, len = rows.length-1; i < len; i++) {
								var row = rows[i];
								if (row.style.display == 'none')
									continue;

								var checkbox = _T(row, 'input', 0);
								var match = checkbox.name && checkbox.name.match(/^id_(\d+)$/);
								if (match) {
									var reportID = match[1];
									if (checkbox.type == 'checkbox' && Data.reports.isOld(reportID)) {
										checkbox.click();
									}
								}
							}
						}, false);
					}

					if (Data.settings.get('reports_searchInput')) {
						Style.reportOverview();

						GUI.filter.initReports();
					}

					var report_list = __('report_list');


					var rows = report_list.rows;

					var firstTH = _T(report_list, 'th', 0);
					var headerRow = firstTH.parentNode;
					var lastRow = rows[rows.length-1];
					var newHeader = _ce('th', texts.gui.DSPlus);
					headerRow.appendChild(newHeader);
					var minRows = 1;
					if (headerRow != lastRow && _T(lastRow, 'th').length) {
						var newHeader = _ce('th');
						lastRow.appendChild(newHeader);
						minRows = 2;
					}
					if (rows.length > minRows) {
						for (var i = 1, len = rows.length; i < len; i++) {
							var row = rows[i];
							var match = row.innerHTML.match(regex.viewId);
							if (match) {
								var ID = match[1];
								var type = Data.reports.getType(ID);
								var dotImg = row.cells[1].children[1];
								if (row.cells.length == 2)
									dotImg = _T(row, 'img', 0);
								var hasDot = dotImg && /graphic\/dots\//.test(dotImg.src);
								var dot_img = '';
								if (type == 'new') {
									if (Data.settings.get('reports_changeLinkStyle') && hasDot)
										row.classList.add('newReport');
									dot_img = Data.settings.get('reports_newReport');
								} else if (type == 'old') {
									if (Data.settings.get('reports_changeLinkStyle') && hasDot)
										row.classList.add('oldReport');
									dot_img = Data.settings.get('reports_oldReport');
								} else if (type == 'spy') {
									if (Data.settings.get('reports_changeLinkStyle'))
										row.classList.add('report');
									dot_img = Data.settings.get('reports_spyReport');
								} else {	// type == 'att'
									if (Data.settings.get('reports_changeLinkStyle'))
										row.classList.add('report');
									dot_img = Data.settings.get('reports_readReport');
								}
								var newCell = row.insertCell(-1);
								newCell.style.textAlign = 'center';
								newCell.innerHTML = '<img src="'+ image_base +'dots/'+ dot_img +'.png" alt="">';

								if (!lib.hasPA && Data.settings.get('freePA')) {
									var dot = Data.reports.getDot(ID);
									if (dotImg && (dot == 'green' || dot == 'yellow')) {
										var loot = Data.reports.getLoot(ID);
										var lootImg = _ce('img', '', {
											'title': '',
											'alt': '',
											'src': image_base +'max_loot/'+ (loot ? '1' : '0') +'.png'
										});
										_after(lootImg, dotImg);

										if (row.cells.length == 3)
											lootImg.style.marginLeft = '5px';
									}
								}
							} else {
								var firstCell = row.cells[0];
								if (firstCell.tagName == 'TD')
									firstCell.colSpan += 1;
							}
						}
					}
				}
			} else if (screen == 'info_player') {
				/*
				 * Infos.init();
				 */
				var matchID = location.href.match(regex.id);
				var contentVal = content_value.innerHTML.trim();
				if ((matchID || __('stats')) && contentVal) {
					// Spieler-Aktenlinks
					var ID;
					if (matchID)
						ID = matchID[1];
					else
						ID = game_data.player.id;
					var ownProfile = (ID == game_data.player.id);
					var playerInfoTable = _T(content_value, 'table', ownProfile ? 2 : 1);
					if (playerInfoTable) {
						var site = Data.settings.get('fileSite');
						var link = [
							'http://{lang}.twstats.com/{world}/index.php?page=player&id={player_id}',
							'http://{world}.twplus.org/file/player/{player_id}/',
							'http://www.dsreal.de/index.php?screen=file&id={player_id}&mode=player&world={world}'
						][site];
						// >>> BETA
						if (game_data.market == 'zz') link = 'http://beta.twstats.com/{world}/index.php?page=player&id={player_id}';
						// <<< BETA
						link = link.replace('{lang}', game_data.market).replace('{world}', game_data.world).replace('{player_id}', ID);
						var HTML = '<td colspan="2"><a href="'+ link +'" target="_blank">'+ lib.guillemetRight +' '+ texts.gui.freePA.playerFile +'</a> ('+ texts.gui.freePA.externalLink[site] +')</td>';
						if (lib.hasPA) {
							var rows = playerInfoTable.rows;
							rows[rows.length-1].innerHTML = HTML;
						} else if (!lib.hasPA && Data.settings.get('freePA')) {
							playerInfoTable.insertRow(-1).innerHTML = HTML;
						}

						if (Data.settings.get('showStats')) {
							var lang = lib.lang;
							var site = Data.settings.get('statSite');
							var playerChartLink = [
								{
									file: 'http://{lang}.twstats.com/{world}/index.php?page=player&id={player_id}',
									conquer: 'http://{lang}.twstats.com/{world}/index.php?page=player&mode=conquers&id={player_id}'
								},
								{
									file: 'http://www.dsreal.de/index.php?world={world}&screen=file&mode=player&id={player_id}',
									conquer: 'http://www.dsreal.de/index.php?world={world}&screen=conquer&mode=player&id={player_id}'
								}
							][site];
							var advanded = false;
							var playerChartSrc = [
								{
									points: 'http://{lang}.twstats.com/{world}/image.php?type=player'+ (advanded ? 'ss' : '') +'graph&graph=points&id={player_id}',
									villages: 'http://{lang}.twstats.com/{world}/image.php?type=player'+ (advanded ? 'ss' : '') +'graph&graph=villages&id={player_id}',
									rank: 'http://{lang}.twstats.com/{world}/image.php?type=playerssgraph&graph=rank&id={player_id}',
									bashall: 'http://{lang}.twstats.com/{world}/image.php?type=player'+ (advanded ? 'ss' : '') +'graph&graph=od&id={player_id}',
									bashoff: 'http://{lang}.twstats.com/{world}/image.php?type=player'+ (advanded ? 'ss' : '') +'graph&graph=oda&id={player_id}',
									bashdef: 'http://{lang}.twstats.com/{world}/image.php?type=player'+ (advanded ? 'ss' : '') +'graph&graph=odd&id={player_id}',
									size: [340, 130]
								},
								{
									points: 'http://www.dsreal.de/charts/playerPoints.php?id={player_id}&world={world}',
									villages: 'http://www.dsreal.de/charts/playerVillages.php?id={player_id}&world={world}',
									rank: 'http://www.dsreal.de/charts/playerRank.php?id={player_id}&world={world}',
									bashall: 'http://www.dsreal.de/charts/playerBashall.php?id={player_id}&world={world}',
									bashoff: 'http://www.dsreal.de/charts/playerBashoff.php?id={player_id}&world={world}',
									bashdef: 'http://www.dsreal.de/charts/playerBashdef.php?id={player_id}&world={world}',
									size: [360, 135]
								}
							][site];
							var parseSrc = function(type) {
								return playerChartSrc[type].replace('{lang}', lang).replace('{world}', game_data.world).replace('{player_id}', ID);
							};
							var getStyle = function() {
								var width = playerChartSrc.size[0];
								var height = playerChartSrc.size[1];
								return	'width: '+ width +'px;' +
										'height: '+ height +'px;' +
										'background: url(\'http://fileface.de/gm/graphics/loading_'+ width +'_'+ height +'.gif\') no-repeat scroll 0 0 transparent';
							};
							var changeChart = function() {
								this.blur();
								var container = this.parentNode;
								while (container.tagName != 'TABLE') {
									container = container.parentNode;
								}
								var imgContainer = _prev(container);
								var type = this.id.replace('dsplus_player_', '');
								var id = 'dsplus_chartPlayer_'+ type;

								var children = imgContainer.children;
								for (var i = 0, child; child = children[i]; i++) {
									_hide(child);
								}
								var img = __(id);
								if (!img) {
									var newImg = _ce('img', '', {
										'id': id,
										'src': parseSrc(type)
									});
									imgContainer.appendChild(newImg);
								} else {
									_show(img);
								}
								var selected = _C(this.parentNode.parentNode, 'selected', 0);
								selected.classList.remove('selected');
								this.parentNode.classList.add('selected');
							};
							if (lang == 'en') lang = 'www';

							var newRow = playerInfoTable.insertRow(-1);
							newRow.style.textAlign = 'center';
							newRow.innerHTML = '<td colspan="2">' +
								'<a target="_blank" href="'+ playerChartLink.file.replace('{lang}', lang).replace('{world}', game_data.world).replace('{player_id}', ID) +'" style="display: block; '+ getStyle() +'">' +
									'<img id="dsplus_chartPlayer_points" src="'+ parseSrc('points') +'">' +
								'</a>' +
								'<table class="vis" style="width: 100%;"><tr class="nowrap">' +
									'<th><u>'+ texts.gui.charts.general +':</u></th>' +
									'<th class="selected">' +
										'<a href="javascript:;" id="dsplus_player_points">'+ lib.guillemetRight +' '+ texts.gui.charts.points +'</a>' +
									'</th>' +
									'<th>' +
										'<a href="javascript:;" id="dsplus_player_villages">'+ lib.guillemetRight +' '+ texts.gui.charts.villages +'</a>' +
									'</th>' +
									'<th>' +
										'<a href="javascript:;" id="dsplus_player_rank">'+ lib.guillemetRight +' '+ texts.gui.charts.rank +'</a>' +
									'</th>' +
								'</tr></table>' +
							'</td>';

							var newRow = playerInfoTable.insertRow(-1);
							newRow.style.textAlign = 'center';
							newRow.innerHTML = '<td colspan="2">' +
								'<a target="_blank" href="'+ playerChartLink.conquer.replace('{lang}', lang).replace('{world}', game_data.world).replace('{player_id}', ID) +'" style="display: block; '+ getStyle() +'">' +
									'<img id="dsplus_chartPlayer_bashall" src="'+ parseSrc('bashall') +'">' +
								'</a>' +
								'<table class="vis" style="width: 100%;"><tr class="nowrap"><tr>' +
									'<th><u>'+ texts.gui.charts.bash +':</u></th>' +
									'<th class="selected">' +
										'<a href="javascript:;" id="dsplus_player_bashall">'+ lib.guillemetRight +' '+ texts.gui.charts.bashall +'</a>' +
									'</th>' +
									'<th>' +
										'<a href="javascript:;" id="dsplus_player_bashoff">'+ lib.guillemetRight +' '+ texts.gui.charts.bashoff +'</a>' +
									'</th>' +
									'<th>' +
										'<a href="javascript:;" id="dsplus_player_bashdef">'+ lib.guillemetRight +' '+ texts.gui.charts.bashdef +'</a>' +
									'</th>' +
								'</tr></table>' +
							'</td>';

							__('dsplus_player_points').addEventListener('click', changeChart, true);
							__('dsplus_player_villages').addEventListener('click', changeChart, true);
							__('dsplus_player_rank').addEventListener('click', changeChart, true);
							__('dsplus_player_bashall').addEventListener('click', changeChart, true);
							__('dsplus_player_bashoff').addEventListener('click', changeChart, true);
							__('dsplus_player_bashdef').addEventListener('click', changeChart, true);
						}
					}
				}
			} else if (screen == 'info_ally') {
			} else if (screen == 'ally') {
			} else if (screen == 'info_village') {
				var matchID = location.href.match(regex.id);
				var contentVal = content_value.innerHTML.trim();
				if (matchID && contentVal) {
					var ID = matchID[1];
					var img = _T(content_value, 'img', 0);
					var imgSrc = img && img.src;
					var specialEvent = (imgSrc && imgSrc.indexOf('/events/') > -1);
					var thirdRow = _T(content_value, 'tr', 2);
					var match = thirdRow.cells[thirdRow.cells.length-1].innerHTML.split('|');
					var xCoord = parseInt(match[0], 10);
					var yCoord = parseInt(match[1], 10);
					var tooltips = false;
					Infos.villageID = ID;

					if (specialEvent) {
						var timer = _C(content_value, 'timer', 0);
						if (timer) {
							var timeSplit = timer.textContent.split(':');
							Data.villages.setEventUntil(ID, (parseInt(timeSplit[0], 10)*60 + parseInt(timeSplit[1], 10))*60 + parseInt(timeSplit[2], 10) + lib.getServerTime());
						}
					}

					// Aktenlinks
					var villageInfoTable = _T(content_value, 'table', 1);
					var site = Data.settings.get('fileSite');
					var link = [
						'http://{lang}.twstats.com/{world}/index.php?page=village&id={village_id}',
						'http://{world}.twplus.org/file/village/{village_id}/',
						'http://www.dsreal.de/index.php?screen=village&id={village_id}&world={world}'
					][site];
					// -> BETA
					if (game_data.market == 'zz') link = 'http://beta.twstats.com/{world}/index.php?page=village&id={village_id}';
					// <- BETA
					link = link.replace('{lang}', game_data.market).replace('{world}', game_data.world).replace('{village_id}', ID);
					var HTML = '<td colspan="2"><a href="'+ link +'" target="_blank">'+ lib.guillemetRight +' '+ texts.gui.freePA.villageFile +'</a> ('+ texts.gui.freePA.externalLink[site] +')</td>';
					if (lib.hasPA && !specialEvent) {
						var rows = villageInfoTable.rows;
						rows[rows.length-1].innerHTML = HTML;
					} else if (lib.hasPA && specialEvent || Data.settings.get('freePA')) {
						villageInfoTable.insertRow(-1).innerHTML = HTML;
					}

					/*
					 * show the runtimes
					 */
					/**/
					var hCoords = lib.getHomeCoords();
					var coordDiff = Math.sqrt(Math.pow(hCoords[0] - xCoord, 2) + Math.pow(hCoords[1] - yCoord, 2));
					if (coordDiff) {
						var firstRow = _T(content_value, 'tr', 0);
						var children = firstRow.children;
						var lastChild = children[children.length-1];
						lastChild.appendChild(Infos.getRuntimesTable(coordDiff));
					}
					/**/

					/*
					 * 'Delete data'-link
					 */
					if (Data.villages.existSpy(ID) || Data.villages.exist(ID)) {
						var firstTH = _T(content_value, 'th', 0);
						var deleteLink = _ce('a', '', {
							'href': 'javascript:;',
							'class': 'cancel-icon solo tooltip float_right',
							'title': texts.gui.delData
						});
						deleteLink.addEventListener('click', function() {
							function delIt() {
								setTimeout(function() {
									var ID = location.href.match(regex.id)[1];
									Data.villages.delData(ID);
									location.reload();
								});
							}
							if (!Data.settings.get('confirm_deleting')) {
								delIt();
							} else {
								var buttons = [{
									text: texts.gui.OK,
									callback: delIt,
									confirm: true
								}];
								UI.ConfirmationBox(texts.gui.delConfirm, buttons);
							}
						}, true);
						firstTH.appendChild(deleteLink);
						tooltips = true;
					}

					/*
					 * Show lock status
					 */
					var lockRow = villageInfoTable.insertRow(-1);
					lockRow.id = 'dsplus_lock';
					var lockCell = lockRow.insertCell(-1);
					lockCell.colSpan = 2;
					var lockCheckbox = _ce('input', '', {
						'type': 'checkbox'
					});
					lockCheckbox.checked = Data.villages.isLocked(ID);
					lockCheckbox.addEventListener('change', function() {
						if (!Data.villages.exist(ID)) {
							Data.villages.setCoords(ID, xCoord +'|'+ yCoord);
							// TODO: save the name and ownerID...
						}
						Data.villages.setLocked(Infos.villageID, this.checked);
						Data.villages.save();
					}, false);
					lockCell.appendChild(lockCheckbox);
					lockCell.appendChild(_text(' '+ texts.gui.lockFarm));

					// read and save the bonus
					var bonus_icon = doc.querySelector('span.bonus_icon');
					var bonus = bonus_icon && bonus_icon.className;
					if (bonus) {
						var match = bonus.match(/bonus_icon_(\d)/);
						Data.villages.setBonus(ID, match[1]);
					}

					/*
					 * calculate the points and insert them
					 */
					if (!specialEvent && Data.villages.existSpy(ID)) {
						var cells = villageInfoTable.rows[2].cells;
						var pointsCell = cells[cells.length-1];
						var momPoints = parseInt(pointsCell.innerHTML.replace(/(<[^>]*>)|\.|\s/g, ''), 10);
						var pointsDiff = momPoints - Data.villages.getPoints(ID);

						// is in the popup?
					//	var points = lib.formatNumber(momPoints, true, true);
						pointsCell.appendChild(_text(' '));
						var pointsSpan = _ce('span');
						pointsSpan.style.fontSize = '90%';
						if (!pointsDiff) {
							pointsSpan.innerHTML = lib.plusMinus +'0';
						} else if (pointsDiff > 0) {
							pointsSpan.style.color = 'green';
							pointsSpan.appendChild(_text('+'+ pointsDiff));
						} else {
							pointsSpan.style.color = 'red';
							pointsSpan.innerHTML = pointsDiff;
						}
						pointsCell.appendChild(pointsSpan);
					}

					/*
					 * for information (resources, loaylty, buildings, units)
					 */
					var infoTable = _ce('table', '', {
						'class': 'vis',
						'style': 'width: 100%;'
					});
					var firstRow = _T(content_value, 'tr', 0);
					var children = firstRow.children;
					children[children.length-1].appendChild(infoTable);
					if (Data.villages.existSpy(ID)) {
						/*
						 * Show the success rate
						 */
						var eqEl;
						var secondTable = _T(content_value, 'table', 1);
						var rows = secondTable.rows;
						for (var i = rows.length-1; i > 0 && !eqEl; i--) {
							var row = rows[i];
							if (row.cells.length > 1)
								eqEl = row;
						}
						var newRow = _ce('tr', '', {
							'class': 'dsplus_eq'
						});
						newRow.insertCell(-1).innerHTML = texts.gui.eq +':';
						var cell = newRow.insertCell(-1);
						cell.innerHTML = '<span class="eqNum">'+ Math.round(Data.villages.getEQ(ID) * 100) +'</span> <i>%</i> <span class="grey eqCount">('+ Data.villages.getEQCount(ID) +')</span><a href="javascript:;" class="cancel-icon solo tooltip float_right" title="'+ texts.gui.resetEQ +'"></a>';
						var lastCell = cell.children[cell.children.length-1];
						lastCell.addEventListener('click', function() {
							var ID = Infos.villageID;
							Data.villages.setEQ(ID, 1);
							Data.villages.setEQCount(ID, 0);
							Data.villages.save();

							var eqNum = _C(cell, 'eqNum', 0);
							eqNum.innerHTML = Math.round(Data.villages.getEQ(ID) * 100);
							var eqCount = _C(cell, 'eqCount', 0);
							eqCount.innerHTML = '('+ Data.villages.getEQCount(ID) +')';
						}, true);
						_after(newRow, eqEl);
						tooltips = true;


						// possible resources:
						Infos.addResourceRow(infoTable, ID);

						// loaylty:
						Infos.addMoodRow(infoTable, ID);

						// buildings:
						Infos.addBuildingRow(infoTable, ID, pointsDiff);
					}
					if (Data.villages.exist(ID)) {
						// units
						Infos.addUnitsRow(infoTable, ID, specialEvent);
					}

					if (tooltips)
						UI.ToolTip(_C(doc, 'tooltip'), {});
				}
			} else if (screen == 'am_farm') {
				if (Data.settings.get('fa_additions_enabled') && __('am_widget_Farm')) {
					PA_Features.fa_initGUI();
					__('am_widget_Farm').addEventListener('DOMNodeInserted', function(e) {
						var originalTarget = e.originalTarget;
						var relatedNode = e.relatedNode;
						if (originalTarget.id != 'dsplus_row' &&
							!jQuery(originalTarget).parents('#dsplus_row').length &&
							relatedNode.tagName != 'TD' &&
							relatedNode.tagName != 'TR')	// Endlosrekursion verhindern
							PA_Features.fa_initGUI();
					}, false);
				}
			} else if (screen == 'info_command') {
				/*
				 * Command.init();
				 */
				var $table = jQuery(content_value).find('table').eq(0);
				$table.find('tr').last().before(
					'<tr><td colspan="3"><a href="javascript:;" id="dsplus_formatAttack">'+ lib.guillemetRight +' '+ texts.gui.commandFormat.format +'</a></td></tr>' +
					'<tr style="display: none;" id="dsplus_formatAttack_textRow"><td colspan="3">' +
						'<textarea style="width: 95%; height: 160px;" id="dsplus_formatAttack_text" onfocus="this.select();">'+ text +'</textarea>' +
						'<input type="checkbox" id="dsplus_formatAttack_forum" checked> '+ texts.gui.commandFormat.forum +
					'</td></tr>'
				);
				var formatAttack = __('dsplus_formatAttack');
				var textRow = __('dsplus_formatAttack_textRow');
				var textarea = __('dsplus_formatAttack_text');
				var checkbox = __('dsplus_formatAttack_forum');

				if (formatAttack && textRow && textarea && checkbox) {
					var format = '[table]\r\n'+
						'[*]'+ texts.gui.commandFormat.origin +'[|]'+ texts.gui.commandFormat.player +':[|]{attacker}' +'\r\n'+
						'[*][|]'+ texts.gui.commandFormat.village +':[|][coord]{attackerCoords}[/coord]' +'\r\n'+
						'[*]'+ texts.gui.commandFormat.target +'[|]'+ texts.gui.commandFormat.player +':[|]{defender}' +'\r\n'+
						'[*][|]'+ texts.gui.commandFormat.village +':[|][coord]{defenderCoords}[/coord]' +'\r\n'+
						'[*]'+ texts.gui.commandFormat.arrival +':[|][|]{arrivalTime}' +'\r\n'+
						'[*]'+ texts.gui.commandFormat.arrivalIn +':[|][|]{arrivalIn}' +'\r\n'+
						'{wall}' +
					'[/table]';
					var wallFormat = '[*][b]'+ texts.gui.commandFormat.wallLevel +':[/b][|][b][color=#{color}]{wallLevel}[/color][/b][|]' +'\r\n';;
					var formatIt = function() {
						var attacker = $table.find('tr').eq(1).find('a').html() || '---';
						var attackerCoords = $table.find('tr').eq(2).find('a').html().match(regex.coords)[1];
						var defender = $table.find('tr').eq(3).find('a').html() || '---';
						var defenderCoords = $table.find('tr').eq(4).find('a').html().match(regex.coords)[1];
						var arrivalTime = null;
						var arrivalIn = null;
						var wall = game_data.village.buildings && game_data.village.buildings.wall;
						var idx = 5;
						if (!/\d+\.\d+\.\d+ \d+:\d+:\d+/.test($table.find('tr').eq(idx).find('td').last().html())) idx++;
						arrivalTime = $table.find('tr').eq(idx).find('td').last().html();
						arrivalIn = $table.find('tr').eq(idx+1).find('td').last().html();
						if (arrivalTime) {
							var spanOpen = '[color=#888888]';
							var spanClose = '[/color]';
							if (checkbox.checked) {
								spanOpen = '[size=8][color=#ff0000]';
								spanClose = '[/color][/size]';
							}
							arrivalTime = arrivalTime.replace('<span class="small grey">', spanOpen).replace('</span>', spanClose);
						}
						if (arrivalIn) arrivalIn = arrivalIn.replace(/<[^>]+>/g, '');
						if (attacker != '---') attacker = '[player]'+ attacker +'[/player]';
						if (defender != '---') defender = '[player]'+ defender +'[/player]';
						if (wall && defender != '---') {
							var defenderID = $table.find('tr').eq(3).find('a').attr('href').match(regex.id)[1];
							if (defenderID == game_data.player.id) {
								wall = parseInt(wall, 10);
								var color = 'ff0000';
								if (wall > 15) color = '23c823';
								else if (wall > 5) color = '0000ff';
								wall = wallFormat.replace('{color}', color).replace('{wallLevel}', wall);
							} else wall = '';
						} else wall = '';
						var text = format.replace('{attacker}', attacker).replace('{attackerCoords}', attackerCoords);
						text = text.replace('{defender}', defender).replace('{defenderCoords}', defenderCoords);
						text = text.replace('{arrivalTime}', arrivalTime);
						text = text.replace('{arrivalIn}', arrivalIn);
						text = text.replace('{wall}', wall);

						textarea.value = text;
						textarea.focus();
					};

					formatAttack.addEventListener('click', function() {
						if (textRow.style.display == 'none') {
							_show(textRow);
							formatIt();
						} else {
							_hide(textRow);
						}
					}, false);
					textarea.addEventListener('focus', function() {
						this.select();
					}, false);
					textarea.addEventListener('dblclick', function() {
						this.select();
					}, false);
					checkbox.addEventListener('click', function() {
						formatIt();
					}, false);
				}
			} else if (screen == 'premium') {
				// add information to gui what DS+ can do
				PA_Features.addInfomarks();
			}

			Storage.colorRes();

			/*
			 * Command.init();
			 */
			if (screen == 'place' && __('units_form') || screen == 'overview' && __('overviewtable')) {
				var commands = [];
				var fromID = game_data.village.id;
			//	var from = game_data.village.coord;
				var ths;
				if (screen == 'place') {
					ths = _T(__('units_form').parentNode, 'th');
				} else {
					ths = _T(__('show_outgoing_units'), 'th');
				}
				var fail = false;

				var rows;
				for (var i = 0, max = ths.length; i < max && !rows; i++) {
					var th = ths[i];
					if (texts.regex.ownCommands.test(th.innerHTML))
						rows = th.parentNode.parentNode.rows;
				}
				if (rows) {
					for (var i = 1, max = rows.length; i < max; i++) {
						var row = rows[i];
						var match;
						var cells = row.cells;
						if (cells.length < 3) continue;
						match = cells[0].innerHTML.match(texts.regex.commands.attack);
						// it is an attack
						if (match) {
							var idx = match[2] + '|' + match[3];
							var actionID = cells[0].innerHTML.match(regex.commandLink);
							var time = texts.locale.timeStr2Sec(cells[1].innerHTML.replace(/<[^>]+>/g, ''), lib);
							var cancel = texts.regex.commands.cancel.test(cells[cells.length - 1].innerHTML);
							// if it is not an attack continue - but what should it be if it is not an attack?
							if (!actionID) continue;
							commands.push([actionID[1], fromID, idx, time, 1, cancel]);
						} else {	// not an attack or the command is renamed
							// renamed command!
							if (regex.attackImg.test(row.innerHTML)) fail = true;
						}
					}
				}
				if (fail)
					lib.error(texts.gui.readAttacksFail);

				// clean the data
				var froms = Data.commands.getFrom(fromID);
				for (var i = 0; i < froms.length; i++) {
					var actionID = froms[i];
					var toID = Data.commands.get(actionID, 'to');
					var tos = Data.commands.getTo(toID);
					var pos = tos.length;
					for (var j = 0; j < pos; j++) {
						if (tos[j] == actionID) {
							Data.commands.spliceTo(toID, j);
							pos = j;
						}
					}
					Data.commands.del(actionID);	// delete actionIDs
				}
				Data.commands.emptyFrom(fromID);

				// renew the data
				for (var i = 0; i < commands.length; i++) {
					var actionID = commands[i][0];
				//	var fromID = commands[i][1];
					var toID = Data.villages.getID(commands[i][2]);
					var time = commands[i][3];
					var type = commands[i][4];
					var cancelable = commands[i][5];
					if (!toID)
						continue;

					Data.commands.addFrom(fromID, actionID);

					var tos = Data.commands.getTo(toID);
					var pos = tos.length;
					for (var j = 0; j < pos; j++) {
						if (tos[j] == actionID) pos = j;
					}
					if (pos == tos.length) Data.commands.addTo(toID, actionID);

					Data.commands.set(actionID, {
						from: fromID,
						to: toID,
						time: time,
						type: type,
						cancel: cancelable
					});
				}

				setTimeout(function() {
					Data.commands.save();
				});
			}

			/*
			 * Infos.init();
			 */
			var matchID = location.href.match(regex.id);
			var contentVal = content_value.innerHTML.trim();
			if ((screen == 'info_ally' && matchID && !game_data.mode || screen == 'ally' && game_data.mode == 'profile') && contentVal) {
				// Stammes-Aktenlinks
				var ID = screen == 'ally' ? game_data.player.ally : matchID[1];
				var site = Data.settings.get('fileSite');
				var link = [
					'http://{lang}.twstats.com/{world}/index.php?page=tribe&id={ally_id}',
					'http://{world}.twplus.org/file/ally/{ally_id}/',
					'http://www.dsreal.de/index.php?screen=file&id={ally_id}&mode=ally&world={world}'
				][site];
				// -> BETA
				if (game_data.market == 'zz') link = 'http://beta.twstats.com/{world}/index.php?page=tribe&id={ally_id}';
				// <- BETA
				link = link.replace('{lang}', game_data.market).replace('{world}', game_data.world).replace('{ally_id}', ID);
				var idx = screen == 'ally' ? 2 : 1;
				var rows = _T(content_value, 'table', idx).rows;
				var beforeRow = rows[rows.length-1];
				while (_C(beforeRow, 'no_bg', 0)) {
					beforeRow = _prev(beforeRow);
				}
				if (_prev(beforeRow).innerHTML.indexOf('mode=apply') > -1)
					beforeRow = _prev(beforeRow);

				var HTML = '<td colspan="2"><a href="'+ link +'" target="_blank">'+ lib.guillemetRight +' '+ texts.gui.freePA.allyFile +'</a> ('+ texts.gui.freePA.externalLink[site] +')</td>';
				if (lib.hasPA) {
					_prev(beforeRow).innerHTML = HTML;
				} else if (!lib.hasPA && Data.settings.get('freePA')) {
					var newRow = _ce('tr', HTML);
					_before(newRow, beforeRow);
				}

				if (Data.settings.get('showStats')) {
					var lang = lib.lang;
					var site = Data.settings.get('statSite');
					var allyChartLink = [
						{
							file: 'http://{lang}.twstats.com/{world}/index.php?page=tribe&id={ally_id}',
							conquer: 'http://{lang}.twstats.com/{world}/index.php?page=tribe&mode=conquers&id={ally_id}'
						},
						{
							file: 'http://www.dsreal.de/index.php?world={world}&screen=file&mode=ally&id={ally_id}',
							conquer: 'http://www.dsreal.de/index.php?world={world}&screen=conquer&mode=ally&id={ally_id}'
						}
					][site];
					var advanded = false;
					var allyChartSrc = [
						{
							points: 'http://{lang}.twstats.com/{world}/image.php?type=tribe'+ (advanded ? 'ss' : '') +'graph&graph=points&id={ally_id}',
							villages: 'http://{lang}.twstats.com/{world}/image.php?type=tribe'+ (advanded ? 'ss' : '') +'graph&graph=villages&id={ally_id}',
							members: 'http://{lang}.twstats.com/{world}/image.php?type=tribe'+ (advanded ? 'ss' : '') +'graph&graph=members&id={ally_id}',
							rank: 'http://{lang}.twstats.com/{world}/image.php?type=tribessgraph&graph=rank&id={ally_id}',
							bashall: 'http://{lang}.twstats.com/{world}/image.php?type=tribe'+ (advanded ? 'ss' : '') +'graph&graph=od&id={ally_id}',
							bashoff: 'http://{lang}.twstats.com/{world}/image.php?type=tribe'+ (advanded ? 'ss' : '') +'graph&graph=oda&id={ally_id}',
							bashdef: 'http://{lang}.twstats.com/{world}/image.php?type=tribe'+ (advanded ? 'ss' : '') +'graph&graph=odd&id={ally_id}',
							size: [340, 130]
						},
						{
							points: 'http://www.dsreal.de/charts/allyPoints.php?id={ally_id}&world={world}',
							villages: 'http://www.dsreal.de/charts/allyVillages.php?id={ally_id}&world={world}',
							rank: 'http://www.dsreal.de/charts/allyRank.php?id={ally_id}&world={world}',
							bashall: 'http://www.dsreal.de/charts/allyBashall.php?id={ally_id}&world={world}',
							bashoff: 'http://www.dsreal.de/charts/allyBashoff.php?id={ally_id}&world={world}',
							bashdef: 'http://www.dsreal.de/charts/allyBashdef.php?id={ally_id}&world={world}',
							size: [360, 135]
						}
					][site];
					var parseSrc = function(type) {
						return allyChartSrc[type].replace('{lang}', lang).replace('{world}', game_data.world).replace('{ally_id}', ID);
					};
					var getStyle = function() {
						var width = allyChartSrc.size[0];
						var height = allyChartSrc.size[1];
						return 'width: '+ width +'px;' +
							'height: '+ height +'px;' +
							'background: url(\'http://fileface.de/gm/graphics/loading_'+ width +'_'+ height +'.gif\') no-repeat scroll 0 0 transparent';
					};
					var changeChart = function() {
						this.blur();
						var container = this.parentNode;
						while (container.tagName != 'TABLE') {
							container = container.parentNode;
						}
						var imgContainer = _prev(container);
						var type = this.id.replace('dsplus_ally_', '');
						var id = 'dsplus_chartAlly_'+ type;

						var children = imgContainer.children;
						for (var i = 0, child; child = children[i]; i++) {
							_hide(child);
						}
						var img = __(id);
						if (!img) {
							var newImg = _ce('img', '', {
								'id': id,
								'src': parseSrc(type)
							});
							imgContainer.appendChild(newImg);
						} else {
							_show(img);
						}
						var selected = _C(this.parentNode.parentNode, 'selected', 0);
						selected.classList.remove('selected');
						this.parentNode.classList.add('selected');
					};
					if (lang == 'en') lang = 'www';

					var newRow = _ce('tr');
					newRow.style.textAlign = 'center';
					newRow.innerHTML = '<td colspan="2">' +
						'<a target="_blank" href="'+ allyChartLink.file.replace('{lang}', lang).replace('{world}', game_data.world).replace('{ally_id}', ID) +'" style="display: block; '+ getStyle() +'">' +
							'<img id="dsplus_chartAlly_points" src="'+ parseSrc('points') +'">' +
						'</a>' +
						'<table class="vis" style="width: 100%;"><tr class="nowrap">' +
							'<th><u>'+ texts.gui.charts.general +':</u></th>' +
							'<th class="selected">' +
								'<a href="javascript:;" id="dsplus_ally_points">'+ lib.guillemetRight +' '+ texts.gui.charts.points +'</a>' +
							'</th>' +
							'<th>' +
								'<a href="javascript:;" id="dsplus_ally_villages">'+ lib.guillemetRight +' '+ texts.gui.charts.villages +'</a>' +
							'</th>' +
							'<th>' +
								'<a href="javascript:;" id="dsplus_ally_rank">'+ lib.guillemetRight +' '+ texts.gui.charts.rank +'</a>' +
							'</th>' +
						'</tr></table>' +
					'</td>';
					_before(newRow, beforeRow);

					var newRow = _ce('tr');
					newRow.style.textAlign = 'center';
					newRow.innerHTML = '<td colspan="2">' +
						'<a target="_blank" href="'+ allyChartLink.conquer.replace('{lang}', lang).replace('{world}', game_data.world).replace('{ally_id}', ID) +'" style="display: block; '+ getStyle() +'">' +
							'<img id="dsplus_chartAlly_bashall" src="'+ parseSrc('bashall') +'">' +
						'</a>' +
						'<table class="vis" style="width: 100%;"><tr class="nowrap">' +
							'<th><u>'+ texts.gui.charts.bash +':</u></th>' +
							'<th class="selected">' +
								'<a href="javascript:;" id="dsplus_ally_bashall">'+ lib.guillemetRight +' '+ texts.gui.charts.bashall +'</a>' +
							'</th>' +
							'<th>' +
								'<a href="javascript:;" id="dsplus_ally_bashoff">'+ lib.guillemetRight +' '+ texts.gui.charts.bashoff +'</a>' +
							'</th>' +
							'<th>' +
								'<a href="javascript:;" id="dsplus_ally_bashdef">'+ lib.guillemetRight +' '+ texts.gui.charts.bashdef +'</a>' +
							'</th>' +
						'</tr></table>' +
					'</td>';
					_before(newRow, beforeRow);

					__('dsplus_ally_points').addEventListener('click', changeChart, true);
					__('dsplus_ally_villages').addEventListener('click', changeChart, true);
					__('dsplus_ally_rank').addEventListener('click', changeChart, true);
					__('dsplus_ally_bashall').addEventListener('click', changeChart, true);
					__('dsplus_ally_bashoff').addEventListener('click', changeChart, true);
					__('dsplus_ally_bashdef').addEventListener('click', changeChart, true);
				}
			}
		}

	}

	/*
	 * Hauptfunktion starten
	 */
	try
	{
		if (win.mobile)
		{
			UI.ErrorMessage(texts.gui.noMobileSupport, 5000);
			return;
		}
		if (__('dsplus_intro_close'))
			return;

		var dynamicLoaded = false;
		var lastVersionCheck = lib.storage.getGlobal('versionCheckTime', 0);
		var now = new Date().getTime();
		var lastUpdateCheckDiff = now - lastVersionCheck;
		var scriptVersion_installed = lib.getScriptVersionNum();
		var oldVersion = (scriptVersion_installed < lib.storage.getGlobal('latestVerion', 99));
		if (lastUpdateCheckDiff > 86400000 || oldVersion)
		{
			lib.ajaxFunc('GET', 'getVersion', null, true, {
				onreadystatechange: function ()
				{
					if (this.readyState != 4 || !this.responseText)
						return;

					var scriptVersion_server = parseFloat(this.responseText);
					if (scriptVersion_installed < scriptVersion_server)
					{
						lib.storage.setGlobal('latestVerion', scriptVersion_server);
						lib.storage.setGlobal('updateAvailable', true);
						lib.error(texts.gui.outdatedScript.replace('%version%', lib.getScriptVersion()), 10000);

						var iframe = _ce('iframe', '', {
							'style': 'display: none; visibility: hidden; height: 0px; width: 0px;'
						});
						doc.body.appendChild(iframe);
						iframe.src = updateURL +'DS+.user.js';
					}
					else
					{
						lib.storage.setGlobal('latestVerion', scriptVersion_installed);
						lib.storage.setGlobal('updateAvailable', false);
						if (oldVersion)
						{
							// the page has not yet been affected by DS+
							dynamicLoaded = false;
							oldVersion = false;
							run();
						}
					}
					lib.storage.setGlobal('versionCheckTime', now);
				}
			});
		}

		if (win.TribalWars)
		{
			win.TribalWars.contentLoaded = function()
			{
				if (win.TribalWars._onLoadHandler)
					win.TribalWars._onLoadHandler();
				win.TribalWars.updateHeader();

				dynamicLoaded = true;
				run();
			};
		}

		run();
	}
	catch(error)
	{
		console.log(error);
		if (typeof(error) == 'object')
		{
			if (error.message)
				error.message = error.message.replace(/^DS\+: /, '');

			if (error.message == 'No GameData found!' && !/\/game\.php/.test(location.href))
				return;

			if (error.message == 'QuotaExceededError')
			{
				UI.ErrorMessage('Your storage exceeded the quota!<br>Please delete/reset some data.', 30000);
			}

			/*
			 * remove private information
			 */
			var name = error.name;
			var message = error.message;
			// filename
			var fileName = error.fileName ? error.fileName.substr(error.fileName.lastIndexOf('/') + 1) : '-';
			var lineNumber = error.lineNumber || '?';
			// Dateinamen (in einem Stapel)
			var stack = error.stack ? error.stack.split('\n') : error.toString();
			// URL
			var url = location.href
				.replace(/([&?])village=[np0-9]+/,'$1village=')
				.replace(/([&?])view=\d+/,'$1view=')
				.replace(/([&?])id=\d+/,'$1id=')
				.replace(/([&?])group=\d+/,'$1group=')
				.replace(/([&?])t=\d+/,'$1t=')
				.replace(/([&?])h=[a-z0-9]+/,'$1h=')
				.replace(/([&?])thread_id=\d+/,'$1thread_id=')
				.replace(/([&?])target=\d+/,'$1target=')
				.replace(/([&?])lit=\d+/,'$1lit=')
				.replace(/#\d{1,3};\d{1,3}/,'#xxx;yyy')
				.replace(/public_report\/[0-9a-f]+/, 'public_report/[...]');

			// Den Stapel an Dateinamen bearbeiten:
			if (!(stack instanceof Array))
				stack = [stack];
			for (var i = 0, max = stack.length; i < max; i++)
			{
				// nur nicht leere Strings bearbeiten
				if (stack[i])
				{
					var pos1 = stack[i].indexOf('://');
					var pos2 = stack[i].lastIndexOf('/');
					if (pos1 != -1 && pos2 != -1)
						stack[i] = stack[i].substr(0, pos1 + 3) + stack[i].substr(pos2 + 1);
				}
			}
			stack = stack.join('\n');

			var data = {
				scriptVersion: lib.getScriptVersion()
				, dataVersion: lib.getDataVersion()
				, forTWVersion: lib.getForTWVersion()
				, forTWBuild: lib.getForTWBuild()
				, twVersion: lib.getTWVersion()
				, twBuild: lib.getTWBuild()
				, name: name
				, message: message
				, fileName: fileName
				, lineNumber: lineNumber
				, stack: stack
				, url: url
				, browser: navigator.userAgent
			};

			GUI.initErrorGUI(data);

			if (error.message)
				error.message = 'DS+:'+ error.message;
		}
		else if (typeof error == 'string')
		{
			error = 'DS+: '+ error.replace(/^DS\+: /, '');
		}
		throw error;
	}
}
